diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index f2959af5260d3252e125c8b1a4ae1ff3dea1da2c..be5b037aa0b7e03aa64496c389b7e3a8e570e4c2 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.211 2009/11/20 20:38:09 tgl Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.212 2009/12/07 05:22:21 tgl Exp $ -->
 <!--
  Documentation of the system catalogs, directed toward PostgreSQL developers
  -->
@@ -1536,11 +1536,7 @@
       <entry><type>bool</type></entry>
       <entry></entry>
       <entry>
-       True if this is a table and it has (or recently had) any
-       indexes. This is set by <command>CREATE INDEX</command>, but
-       not cleared immediately by <command>DROP INDEX</command>.
-       <command>VACUUM</command> clears <structfield>relhasindex</> if it finds the
-       table has no indexes
+       True if this is a table and it has (or recently had) any indexes
       </entry>
      </row>
 
@@ -1617,6 +1613,17 @@
       </entry>
      </row>
 
+     <row>
+      <entry><structfield>relhasexclusion</structfield></entry>
+      <entry><type>bool</type></entry>
+      <entry></entry>
+      <entry>
+       For a table, true if the table has (or once had) any exclusion
+       constraints; for an index, true if the index supports an exclusion
+       constraint
+      </entry>
+     </row>
+
      <row>
       <entry><structfield>relhasrules</structfield></entry>
       <entry><type>bool</type></entry>
@@ -1680,6 +1687,17 @@
     </tbody>
    </tgroup>
   </table>
+
+  <para>
+   Several of the boolean flags in <structname>pg_class</> are maintained
+   lazily: they are guaranteed to be true if that's the correct state, but
+   may not be reset to false immediately when the condition is no longer
+   true.  For example, <structfield>relhasindex</> is set by
+   <command>CREATE INDEX</command>, but it is never cleared by
+   <command>DROP INDEX</command>.  Instead, <command>VACUUM</command> clears
+   <structfield>relhasindex</> if it finds the table has no indexes.  This
+   arrangement avoids race conditions and improves concurrency.
+  </para>
  </sect1>
 
  <sect1 id="catalog-pg-constraint">
@@ -1690,11 +1708,12 @@
   </indexterm>
 
   <para>
-   The catalog <structname>pg_constraint</structname> stores check, primary key, unique, and foreign
-   key constraints on tables.  (Column constraints are not treated
-   specially.  Every column constraint is equivalent to some table
-   constraint.)  Not-null constraints are represented in the
-   <structname>pg_attribute</> catalog.
+   The catalog <structname>pg_constraint</structname> stores check, primary
+   key, unique, foreign key, and exclusion constraints on tables.
+   (Column constraints are not treated specially.  Every column constraint is
+   equivalent to some table constraint.)
+   Not-null constraints are represented in the <structname>pg_attribute</>
+   catalog, not here.
   </para>
 
   <para>
@@ -1739,7 +1758,8 @@
         <literal>c</> = check constraint,
         <literal>f</> = foreign key constraint,
         <literal>p</> = primary key constraint,
-        <literal>u</> = unique constraint
+        <literal>u</> = unique constraint,
+        <literal>x</> = exclusion constraint
       </entry>
      </row>
 
@@ -1776,7 +1796,7 @@
       <entry><type>oid</type></entry>
       <entry><literal><link linkend="catalog-pg-class"><structname>pg_class</structname></link>.oid</literal></entry>
       <entry>The index supporting this constraint, if it's a unique, primary
-       key, or foreign key constraint; else 0</entry>
+       key, foreign key, or exclusion constraint; else 0</entry>
      </row>
 
      <row>
@@ -1828,7 +1848,7 @@
       <entry><type>bool</type></entry>
       <entry></entry>
       <entry>
-       This constraint is defined locally in the relation.  Note that a
+       This constraint is defined locally for the relation.  Note that a
        constraint can be locally defined and inherited simultaneously
       </entry>
      </row>
@@ -1838,7 +1858,8 @@
       <entry><type>int4</type></entry>
       <entry></entry>
       <entry>
-       The number of direct ancestors this constraint has.  A constraint with
+       The number of direct inheritance ancestors this constraint has.
+       A constraint with
        a nonzero number of ancestors cannot be dropped nor renamed
       </entry>
      </row>
@@ -1878,6 +1899,13 @@
       <entry>If a foreign key, list of the equality operators for FK = FK comparisons</entry>
      </row>
 
+     <row>
+      <entry><structfield>conexclop</structfield></entry>
+      <entry><type>oid[]</type></entry>
+      <entry><literal><link linkend="catalog-pg-operator"><structname>pg_operator</structname></link>.oid</></entry>
+      <entry>If an exclusion constraint, list of the per-column exclusion operators</entry>
+     </row>
+
      <row>
       <entry><structfield>conbin</structfield></entry>
       <entry><type>text</type></entry>
@@ -1895,6 +1923,16 @@
    </tgroup>
   </table>
 
+  <para>
+   In the case of an exclusion constraint, <structfield>conkey</structfield>
+   is only useful for constraint elements that are simple column references.
+   For other cases, a zero appears in <structfield>conkey</structfield>
+   and the associated index must be consulted to discover the expression
+   that is constrained.  (<structfield>conkey</structfield> thus has the
+   same contents as <structname>pg_index</>.<structfield>indkey</> for the
+   index.)
+  </para>
+
   <note>
    <para>
     <structfield>consrc</structfield> is not updated when referenced objects
@@ -1908,7 +1946,8 @@
    <para>
     <literal>pg_class.relchecks</literal> needs to agree with the
     number of check-constraint entries found in this table for each
-    relation.
+    relation.  Also, <literal>pg_class.relhasexclusion</literal> must
+    be true if there are any exclusion-constraint entries for the relation.
    </para>
   </note>
 
diff --git a/doc/src/sgml/errcodes.sgml b/doc/src/sgml/errcodes.sgml
index e5597f200438190be8120cdd0413d4f2695a86fa..5819004f48a0af3f6f8f6d1ff23df816dc3f6852 100644
--- a/doc/src/sgml/errcodes.sgml
+++ b/doc/src/sgml/errcodes.sgml
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/errcodes.sgml,v 1.27 2009/03/04 10:55:00 petere Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/errcodes.sgml,v 1.28 2009/12/07 05:22:21 tgl Exp $ -->
 
 <appendix id="errcodes-appendix">
  <title><productname>PostgreSQL</productname> Error Codes</title>
@@ -640,6 +640,12 @@
 <entry>check_violation</entry>
 </row>
 
+<row>
+<entry><literal>23P01</literal></entry>
+<entry>EXCLUSION VIOLATION</entry>
+<entry>exclusion_violation</entry>
+</row>
+
 
 <row>
 <entry spanname="span13"><emphasis role="bold">Class 24 &mdash; Invalid Cursor State</></entry>
diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index 8e4b1bbc3129a26e52e853b605d7762e8d971a97..e315843187c9937dfe5110099315caa4f335c175 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/create_table.sgml,v 1.119 2009/10/27 13:58:28 alvherre Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/create_table.sgml,v 1.120 2009/12/07 05:22:21 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -24,7 +24,7 @@ PostgreSQL documentation
 CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } ] TABLE <replaceable class="PARAMETER">table_name</replaceable> ( [
   { <replaceable class="PARAMETER">column_name</replaceable> <replaceable class="PARAMETER">data_type</replaceable> [ DEFAULT <replaceable>default_expr</replaceable> ] [ <replaceable class="PARAMETER">column_constraint</replaceable> [ ... ] ]
     | <replaceable>table_constraint</replaceable>
-    | LIKE <replaceable>parent_table</replaceable> [ { INCLUDING | EXCLUDING } { DEFAULTS | CONSTRAINTS | INDEXES | STORAGE | COMMENTS | ALL } ] ... }
+    | LIKE <replaceable>parent_table</replaceable> [ <replaceable>like_option</replaceable> ... ] }
     [, ... ]
 ] )
 [ INHERITS ( <replaceable>parent_table</replaceable> [, ... ] ) ]
@@ -37,9 +37,9 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } ] TABLE <replaceable class="PAR
 [ CONSTRAINT <replaceable class="PARAMETER">constraint_name</replaceable> ]
 { NOT NULL |
   NULL |
+  CHECK ( <replaceable class="PARAMETER">expression</replaceable> ) |
   UNIQUE <replaceable class="PARAMETER">index_parameters</replaceable> |
   PRIMARY KEY <replaceable class="PARAMETER">index_parameters</replaceable> |
-  CHECK ( <replaceable class="PARAMETER">expression</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 ]
@@ -47,17 +47,26 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } ] TABLE <replaceable class="PAR
 <phrase>and <replaceable class="PARAMETER">table_constraint</replaceable> is:</phrase>
 
 [ CONSTRAINT <replaceable class="PARAMETER">constraint_name</replaceable> ]
-{ UNIQUE ( <replaceable class="PARAMETER">column_name</replaceable> [, ... ] ) <replaceable class="PARAMETER">index_parameters</replaceable> |
+{ CHECK ( <replaceable class="PARAMETER">expression</replaceable> ) |
+  UNIQUE ( <replaceable class="PARAMETER">column_name</replaceable> [, ... ] ) <replaceable class="PARAMETER">index_parameters</replaceable> |
   PRIMARY KEY ( <replaceable class="PARAMETER">column_name</replaceable> [, ... ] ) <replaceable class="PARAMETER">index_parameters</replaceable> |
-  CHECK ( <replaceable class="PARAMETER">expression</replaceable> ) |
+  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 ]
 
-<phrase><replaceable class="PARAMETER">index_parameters</replaceable> in <literal>UNIQUE</literal> and <literal>PRIMARY KEY</literal> constraints are:</phrase>
+<phrase>and <replaceable class="PARAMETER">like_option</replaceable> is:</phrase>
+
+{ INCLUDING | EXCLUDING } { DEFAULTS | CONSTRAINTS | INDEXES | STORAGE | COMMENTS | ALL }
+
+<phrase><replaceable class="PARAMETER">index_parameters</replaceable> in <literal>UNIQUE</literal>, <literal>PRIMARY KEY</literal>, and <literal>EXCLUDE</literal> constraints are:</phrase>
 
 [ WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] ) ]
 [ USING INDEX TABLESPACE <replaceable class="PARAMETER">tablespace</replaceable> ]
+
+<phrase><replaceable class="PARAMETER">exclude_element</replaceable> in an <literal>EXCLUDE</literal> constraint is:</phrase>
+
+{ <replaceable class="parameter">column</replaceable> | ( <replaceable class="parameter">expression</replaceable> ) } [ <replaceable class="parameter">opclass</replaceable> ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ]
 </synopsis>
 
  </refsynopsisdiv>
@@ -251,7 +260,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } ] TABLE <replaceable class="PAR
    </varlistentry>
 
    <varlistentry>
-    <term><literal>LIKE <replaceable>parent_table</replaceable> [ { INCLUDING | EXCLUDING } { DEFAULTS | CONSTRAINTS | INDEXES | STORAGE | COMMENTS | ALL } ]</literal></term>
+    <term><literal>LIKE <replaceable>parent_table</replaceable> [ <replaceable>like_option</replaceable> ... ]</literal></term>
     <listitem>
      <para>
       The <literal>LIKE</literal> clause specifies a table from which
@@ -350,6 +359,29 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } ] TABLE <replaceable class="PAR
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><literal>CHECK ( <replaceable class="PARAMETER">expression</replaceable> )</literal></term>
+    <listitem>
+     <para>
+      The <literal>CHECK</> clause specifies an expression producing a
+      Boolean result which new or updated rows must satisfy for an
+      insert or update operation to succeed.  Expressions evaluating
+      to TRUE or UNKNOWN succeed.  Should any row of an insert or
+      update operation produce a FALSE result an error exception is
+      raised and the insert or update does not alter the database.  A
+      check constraint specified as a column constraint should
+      reference that column's value only, while an expression
+      appearing in a table constraint can reference multiple columns.
+     </para>
+
+     <para>
+      Currently, <literal>CHECK</literal> expressions cannot contain
+      subqueries nor refer to variables other than columns of the
+      current row.
+     </para>
+    </listitem>
+   </varlistentry>
+
    <varlistentry>
     <term><literal>UNIQUE</> (column constraint)</term>
     <term><literal>UNIQUE ( <replaceable class="PARAMETER">column_name</replaceable> [, ... ] )</> (table constraint)</term>
@@ -406,29 +438,54 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } ] TABLE <replaceable class="PAR
    </varlistentry>
 
    <varlistentry>
-    <term><literal>CHECK ( <replaceable class="PARAMETER">expression</replaceable> )</literal></term>
+    <term><literal>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> ) ]</literal></term>
     <listitem>
      <para>
-      The <literal>CHECK</> clause specifies an expression producing a
-      Boolean result which new or updated rows must satisfy for an
-      insert or update operation to succeed.  Expressions evaluating
-      to TRUE or UNKNOWN succeed.  Should any row of an insert or
-      update operation produce a FALSE result an error exception is
-      raised and the insert or update does not alter the database.  A
-      check constraint specified as a column constraint should
-      reference that column's value only, while an expression
-      appearing in a table constraint can reference multiple columns.
+      The <literal>EXCLUDE</> clause defines an exclusion
+      constraint, which guarantees that if
+      any two rows are compared on the specified column(s) or
+      expression(s) using the specified operator(s), not all of these
+      comparisons will return <literal>TRUE</>.  If all of the
+      specified operators test for equality, this is equivalent to a
+      <literal>UNIQUE</> constraint, although an ordinary unique constraint
+      will be faster.  However, exclusion constraints can specify
+      constraints that are more general than simple equality.
+      For example, you can specify a constraint that
+      no two rows in the table contain overlapping circles
+      (see <xref linkend="datatype-geometric">) by using the
+      <literal>&&</> operator.
      </para>
 
      <para>
-      Currently, <literal>CHECK</literal> expressions cannot contain
-      subqueries nor refer to variables other than columns of the
-      current row.
+      Exclusion constraints are implemented using
+      an index, so each specified operator must be associated with an
+      appropriate operator class
+      (see <xref linkend="indexes-opclass">) for the index access
+      method <replaceable>index_method</>.
+      The operators are required to be commutative.
+      Each <replaceable class="parameter">exclude_element</replaceable>
+      can optionally specify an operator class and/or ordering options;
+      these are described fully under
+      <xref linkend="sql-createindex" endterm="sql-createindex-title">.
+     </para>
+
+     <para>
+      The access method must support <literal>amgettuple</> (see <xref
+      linkend="indexam">); at present this means <acronym>GIN</>
+      cannot be used.  Although it's allowed, there is little point in using
+      btree or hash indexes with an exclusion constraint, because this
+      does nothing that an ordinary unique constraint doesn't do better.
+      So in practice the access method will always be <acronym>GiST</>.
+     </para>
+
+     <para>
+      The <replaceable class="parameter">predicate</> allows you to specify an
+      exclusion constraint on a subset of the table; internally this creates a
+      partial index. Note that parentheses are required around the predicate.
      </para>
     </listitem>
    </varlistentry>
 
-
    <varlistentry>
     <term><literal>REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> ) ] [ MATCH <replaceable class="parameter">matchtype</replaceable> ] [ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ]</literal> (column constraint)</term>
 
@@ -557,7 +614,8 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } ] TABLE <replaceable class="PAR
       deferrable can be postponed until the end of the transaction
       (using the <xref linkend="sql-set-constraints" endterm="sql-set-constraints-title"> command).
       <literal>NOT DEFERRABLE</literal> is the default.
-      Currently, only <literal>UNIQUE</>, <literal>PRIMARY KEY</>, and
+      Currently, only <literal>UNIQUE</>, <literal>PRIMARY KEY</>,
+      <literal>EXCLUDE</>, and
       <literal>REFERENCES</> (foreign key) constraints accept this
       clause.  <literal>NOT NULL</> and <literal>CHECK</> constraints are not
       deferrable.
@@ -695,8 +753,8 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } ] TABLE <replaceable class="PAR
     <listitem>
      <para>
       This clause allows selection of the tablespace in which the index
-      associated with a <literal>UNIQUE</literal> or <literal>PRIMARY
-      KEY</literal> constraint will be created.
+      associated with a <literal>UNIQUE</literal>, <literal>PRIMARY
+      KEY</literal>, or <literal>EXCLUDE</> constraint will be created.
       If not specified,
       <xref linkend="guc-default-tablespace"> is consulted, or
       <xref linkend="guc-temp-tablespaces"> if the table is temporary.
@@ -715,8 +773,9 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } ] TABLE <replaceable class="PAR
 
    <para>
     The <literal>WITH</> clause can specify <firstterm>storage parameters</>
-    for tables, and for indexes associated with a <literal>UNIQUE</literal> or
-    <literal>PRIMARY KEY</literal> constraint.  Storage parameters for
+    for tables, and for indexes associated with a <literal>UNIQUE</literal>,
+    <literal>PRIMARY KEY</literal>, or <literal>EXCLUDE</> constraint.
+    Storage parameters for
     indexes are documented in <xref linkend="SQL-CREATEINDEX"
     endterm="sql-createindex-title">.  The storage parameters currently
     available for tables are listed below.  For each parameter, unless noted,
@@ -1099,6 +1158,18 @@ WITH (fillfactor=70);
 </programlisting>
   </para>
 
+  <para>
+   Create table <structname>circles</> with an exclusion
+   constraint that prevents any two circles from overlapping:
+
+<programlisting>
+CREATE TABLE circles (
+    c circle,
+    EXCLUDE USING gist (c WITH &&)
+);
+</programlisting>
+  </para>
+
   <para>
    Create table <structname>cinemas</> in tablespace <structname>diskvol1</>:
 
@@ -1194,6 +1265,15 @@ CREATE TABLE cinemas (
    </para>
   </refsect2>
 
+  <refsect2>
+   <title><literal>EXCLUDE</literal> Constraint</title>
+
+   <para>
+    The <literal>EXCLUDE</> constraint type is a
+    <productname>PostgreSQL</productname> extension.
+   </para>
+  </refsect2>
+
   <refsect2>
    <title><literal>NULL</literal> <quote>Constraint</quote></title>
 
diff --git a/src/backend/access/index/genam.c b/src/backend/access/index/genam.c
index 860e576603435f8ba201173c41a54c465d83f88f..f07996a3d46c76eb49111670a891d65788c22d22 100644
--- a/src/backend/access/index/genam.c
+++ b/src/backend/access/index/genam.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/access/index/genam.c,v 1.76 2009/08/01 20:59:17 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/access/index/genam.c,v 1.77 2009/12/07 05:22:21 tgl Exp $
  *
  * NOTES
  *	  many of the old access method routines have been turned into
@@ -137,21 +137,18 @@ IndexScanEnd(IndexScanDesc scan)
  *
  * Construct a string describing the contents of an index entry, in the
  * form "(key_name, ...)=(key_value, ...)".  This is currently used
- * only for building unique-constraint error messages, but we don't want
- * to hardwire the spelling of the messages here.
+ * for building unique-constraint and exclusion-constraint error messages.
+ *
+ * The passed-in values/nulls arrays are the "raw" input to the index AM,
+ * e.g. results of FormIndexDatum --- this is not necessarily what is stored
+ * in the index, but it's what the user perceives to be stored.
  */
 char *
 BuildIndexValueDescription(Relation indexRelation,
 						   Datum *values, bool *isnull)
 {
-	/*
-	 * XXX for the moment we use the index's tupdesc as a guide to the
-	 * datatypes of the values.  This is okay for btree indexes but is in
-	 * fact the wrong thing in general.  This will have to be fixed if we
-	 * are ever to support non-btree unique indexes.
-	 */
-	TupleDesc	tupdesc = RelationGetDescr(indexRelation);
 	StringInfoData buf;
+	int			natts = indexRelation->rd_rel->relnatts;
 	int			i;
 
 	initStringInfo(&buf);
@@ -159,7 +156,7 @@ BuildIndexValueDescription(Relation indexRelation,
 					 pg_get_indexdef_columns(RelationGetRelid(indexRelation),
 											 true));
 
-	for (i = 0; i < tupdesc->natts; i++)
+	for (i = 0; i < natts; i++)
 	{
 		char   *val;
 
@@ -170,7 +167,17 @@ BuildIndexValueDescription(Relation indexRelation,
 			Oid		foutoid;
 			bool	typisvarlena;
 
-			getTypeOutputInfo(tupdesc->attrs[i]->atttypid,
+			/*
+			 * The provided data is not necessarily of the type stored in
+			 * the index; rather it is of the index opclass's input type.
+			 * So look at rd_opcintype not the index tupdesc.
+			 *
+			 * Note: this is a bit shaky for opclasses that have pseudotype
+			 * input types such as ANYARRAY or RECORD.  Currently, the
+			 * typoutput functions associated with the pseudotypes will
+			 * work okay, but we might have to try harder in future.
+			 */
+			getTypeOutputInfo(indexRelation->rd_opcintype[i],
 							  &foutoid, &typisvarlena);
 			val = OidOutputFunctionCall(foutoid, values[i]);
 		}
diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y
index 9d6f854d126bf4366ed7dd5a73aeadf91dae1a0e..18affeb098858f141f39a2cc15e9fbac2f54b61c 100644
--- a/src/backend/bootstrap/bootparse.y
+++ b/src/backend/bootstrap/bootparse.y
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/bootstrap/bootparse.y,v 1.100 2009/10/05 19:24:34 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/bootstrap/bootparse.y,v 1.101 2009/12/07 05:22:21 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -267,7 +267,7 @@ Boot_DeclareIndexStmt:
 								$8,
 								NULL,
 								$10,
-								NULL, NIL,
+								NULL, NIL, NIL,
 								false, false, false, false, false,
 								false, false, true, false, false);
 					do_end();
@@ -285,7 +285,7 @@ Boot_DeclareUniqueIndexStmt:
 								$9,
 								NULL,
 								$11,
-								NULL, NIL,
+								NULL, NIL, NIL,
 								true, false, false, false, false,
 								false, false, true, false, false);
 					do_end();
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index 89991ea2fb79b9136bc4e911ac5223b4eabddfcb..5fa07d6c2ae9133da874e3eecf11c6040617f944 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/bootstrap/bootstrap.c,v 1.253 2009/09/27 01:32:11 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/bootstrap/bootstrap.c,v 1.254 2009/12/07 05:22:21 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1100,6 +1100,10 @@ index_register(Oid heap,
 	newind->il_info->ii_Predicate = (List *)
 		copyObject(indexInfo->ii_Predicate);
 	newind->il_info->ii_PredicateState = NIL;
+	/* no exclusion constraints at bootstrap time, so no need to copy */
+	Assert(indexInfo->ii_ExclusionOps == NULL);
+	Assert(indexInfo->ii_ExclusionProcs == NULL);
+	Assert(indexInfo->ii_ExclusionStrats == NULL);
 
 	newind->il_next = ILHead;
 	ILHead = newind;
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index a013306284700ed21dd4a0530f59ad474cbe16cc..8b8fcea734321309cfe3fa9d01e3e9caa34d3dd1 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.360 2009/10/05 19:24:35 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.361 2009/12/07 05:22:21 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -678,6 +678,7 @@ InsertPgClassTuple(Relation pg_class_desc,
 	values[Anum_pg_class_relchecks - 1] = Int16GetDatum(rd_rel->relchecks);
 	values[Anum_pg_class_relhasoids - 1] = BoolGetDatum(rd_rel->relhasoids);
 	values[Anum_pg_class_relhaspkey - 1] = BoolGetDatum(rd_rel->relhaspkey);
+	values[Anum_pg_class_relhasexclusion - 1] = BoolGetDatum(rd_rel->relhasexclusion);
 	values[Anum_pg_class_relhasrules - 1] = BoolGetDatum(rd_rel->relhasrules);
 	values[Anum_pg_class_relhastriggers - 1] = BoolGetDatum(rd_rel->relhastriggers);
 	values[Anum_pg_class_relhassubclass - 1] = BoolGetDatum(rd_rel->relhassubclass);
@@ -1748,9 +1749,10 @@ StoreRelCheck(Relation rel, char *ccname, Node *expr,
 						  ' ',
 						  ' ',
 						  ' ',
-						  expr, /* Tree form check constraint */
-						  ccbin,	/* Binary form check constraint */
-						  ccsrc,	/* Source form check constraint */
+						  NULL,	/* not an exclusion constraint */
+						  expr, /* Tree form of check constraint */
+						  ccbin,	/* Binary form of check constraint */
+						  ccsrc,	/* Source form of check constraint */
 						  is_local,		/* conislocal */
 						  inhcount);	/* coninhcount */
 
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 482d40542dd5886a591c53213414e5cc675e5747..6564c05c9e9710f70730de4aec1b813ad3fa5e38 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.324 2009/11/20 20:38:09 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.325 2009/12/07 05:22:21 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -93,8 +93,12 @@ static void UpdateIndexRelation(Oid indexoid, Oid heapoid,
 					bool primary,
 					bool immediate,
 					bool isvalid);
-static void index_update_stats(Relation rel, bool hasindex, bool isprimary,
+static void index_update_stats(Relation rel,
+				   bool hasindex, bool isprimary, bool hasexclusion,
 				   Oid reltoastidxid, double reltuples);
+static void IndexCheckExclusion(Relation heapRelation,
+					Relation indexRelation,
+					IndexInfo *indexInfo);
 static bool validate_index_callback(ItemPointer itemptr, void *opaque);
 static void validate_index_heapscan(Relation heapRelation,
 						Relation indexRelation,
@@ -505,7 +509,7 @@ UpdateIndexRelation(Oid indexoid,
  *		will be marked "invalid" and the caller must take additional steps
  *		to fix it up.
  *
- * Returns OID of the created index.
+ * Returns the OID of the created index.
  */
 Oid
 index_create(Oid heapRelationId,
@@ -530,9 +534,12 @@ index_create(Oid heapRelationId,
 	Relation	indexRelation;
 	TupleDesc	indexTupDesc;
 	bool		shared_relation;
+	bool		is_exclusion;
 	Oid			namespaceId;
 	int			i;
 
+	is_exclusion = (indexInfo->ii_ExclusionOps != NULL);
+
 	pg_class = heap_open(RelationRelationId, RowExclusiveLock);
 
 	/*
@@ -573,6 +580,15 @@ index_create(Oid heapRelationId,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("concurrent index creation on system catalog tables is not supported")));
 
+	/*
+	 * This case is currently not supported, but there's no way to ask for
+	 * it in the grammar anyway, so it can't happen.
+	 */
+	if (concurrent && is_exclusion)
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg_internal("concurrent index creation for exclusion constraints is not supported")));
+
 	/*
 	 * We cannot allow indexing a shared relation after initdb (because
 	 * there's no way to make the entry in other databases' pg_class).
@@ -658,6 +674,7 @@ index_create(Oid heapRelationId,
 	indexRelation->rd_rel->relam = accessMethodObjectId;
 	indexRelation->rd_rel->relkind = RELKIND_INDEX;
 	indexRelation->rd_rel->relhasoids = false;
+	indexRelation->rd_rel->relhasexclusion = is_exclusion;
 
 	/*
 	 * store index's pg_class entry
@@ -728,14 +745,17 @@ index_create(Oid heapRelationId,
 				constraintType = CONSTRAINT_PRIMARY;
 			else if (indexInfo->ii_Unique)
 				constraintType = CONSTRAINT_UNIQUE;
+			else if (is_exclusion)
+				constraintType = CONSTRAINT_EXCLUSION;
 			else
 			{
-				elog(ERROR, "constraint must be PRIMARY or UNIQUE");
+				elog(ERROR, "constraint must be PRIMARY, UNIQUE or EXCLUDE");
 				constraintType = 0;		/* keep compiler quiet */
 			}
 
-			/* Shouldn't have any expressions */
-			if (indexInfo->ii_Expressions)
+			/* primary/unique constraints shouldn't have any expressions */
+			if (indexInfo->ii_Expressions &&
+				constraintType != CONSTRAINT_EXCLUSION)
 				elog(ERROR, "constraints cannot have index expressions");
 
 			conOid = CreateConstraintEntry(indexRelationName,
@@ -757,6 +777,7 @@ index_create(Oid heapRelationId,
 										   ' ',
 										   ' ',
 										   ' ',
+										   indexInfo->ii_ExclusionOps,
 										   NULL,		/* no check constraint */
 										   NULL,
 										   NULL,
@@ -925,6 +946,7 @@ index_create(Oid heapRelationId,
 		index_update_stats(heapRelation,
 						   true,
 						   isprimary,
+						   is_exclusion,
 						   InvalidOid,
 						   heapRelation->rd_rel->reltuples);
 		/* Make the above update visible */
@@ -1080,6 +1102,21 @@ BuildIndexInfo(Relation index)
 	ii->ii_Predicate = RelationGetIndexPredicate(index);
 	ii->ii_PredicateState = NIL;
 
+	/* fetch exclusion constraint info if any */
+	if (index->rd_rel->relhasexclusion)
+	{
+		RelationGetExclusionInfo(index,
+								 &ii->ii_ExclusionOps,
+								 &ii->ii_ExclusionProcs,
+								 &ii->ii_ExclusionStrats);
+	}
+	else
+	{
+		ii->ii_ExclusionOps = NULL;
+		ii->ii_ExclusionProcs = NULL;
+		ii->ii_ExclusionStrats = NULL;
+	}
+
 	/* other info */
 	ii->ii_Unique = indexStruct->indisunique;
 	ii->ii_ReadyForInserts = indexStruct->indisready;
@@ -1177,6 +1214,7 @@ FormIndexDatum(IndexInfo *indexInfo,
  *
  * hasindex: set relhasindex to this value
  * isprimary: if true, set relhaspkey true; else no change
+ * hasexclusion: if true, set relhasexclusion true; else no change
  * reltoastidxid: if not InvalidOid, set reltoastidxid to this value;
  *		else no change
  * reltuples: set reltuples to this value
@@ -1192,7 +1230,8 @@ FormIndexDatum(IndexInfo *indexInfo,
  * expect a relcache flush to occur after REINDEX.
  */
 static void
-index_update_stats(Relation rel, bool hasindex, bool isprimary,
+index_update_stats(Relation rel,
+				   bool hasindex, bool isprimary, bool hasexclusion,
 				   Oid reltoastidxid, double reltuples)
 {
 	BlockNumber relpages = RelationGetNumberOfBlocks(rel);
@@ -1231,8 +1270,9 @@ index_update_stats(Relation rel, bool hasindex, bool isprimary,
 	 * It is safe to use a non-transactional update even though our
 	 * transaction could still fail before committing.	Setting relhasindex
 	 * true is safe even if there are no indexes (VACUUM will eventually fix
-	 * it), and of course the relpages and reltuples counts are correct (or at
-	 * least more so than the old values) regardless.
+	 * it), likewise for relhaspkey and relhasexclusion.  And of course the
+	 * relpages and reltuples counts are correct (or at least more so than the
+	 * old values) regardless.
 	 */
 
 	pg_class = heap_open(RelationRelationId, RowExclusiveLock);
@@ -1287,6 +1327,14 @@ index_update_stats(Relation rel, bool hasindex, bool isprimary,
 			dirty = true;
 		}
 	}
+	if (hasexclusion)
+	{
+		if (!rd_rel->relhasexclusion)
+		{
+			rd_rel->relhasexclusion = true;
+			dirty = true;
+		}
+	}
 	if (OidIsValid(reltoastidxid))
 	{
 		Assert(rd_rel->relkind == RELKIND_TOASTVALUE);
@@ -1461,6 +1509,13 @@ index_build(Relation heapRelation,
 										 PointerGetDatum(indexInfo)));
 	Assert(PointerIsValid(stats));
 
+	/*
+	 * If it's for an exclusion constraint, make a second pass over the
+	 * heap to verify that the constraint is satisfied.
+	 */
+	if (indexInfo->ii_ExclusionOps != NULL)
+		IndexCheckExclusion(heapRelation, indexRelation, indexInfo);
+
 	/* Restore userid */
 	SetUserIdAndContext(save_userid, save_secdefcxt);
 
@@ -1499,11 +1554,13 @@ index_build(Relation heapRelation,
 	index_update_stats(heapRelation,
 					   true,
 					   isprimary,
+					   (indexInfo->ii_ExclusionOps != NULL),
 					   (heapRelation->rd_rel->relkind == RELKIND_TOASTVALUE) ?
 					   RelationGetRelid(indexRelation) : InvalidOid,
 					   stats->heap_tuples);
 
 	index_update_stats(indexRelation,
+					   false,
 					   false,
 					   false,
 					   InvalidOid,
@@ -1522,15 +1579,14 @@ index_build(Relation heapRelation,
  * is scanned to find tuples that should be entered into the index.  Each
  * such tuple is passed to the AM's callback routine, which does the right
  * things to add it to the new index.  After we return, the AM's index
- * build procedure does whatever cleanup is needed; in particular, it should
- * close the heap and index relations.
+ * build procedure does whatever cleanup it needs.
  *
  * The total count of heap tuples is returned.	This is for updating pg_class
- * statistics.	(It's annoying not to be able to do that here, but we can't
- * do it until after the relation is closed.)  Note that the index AM itself
- * must keep track of the number of index tuples; we don't do so here because
- * the AM might reject some of the tuples for its own reasons, such as being
- * unable to store NULLs.
+ * statistics.	(It's annoying not to be able to do that here, but we want
+ * to merge that update with others; see index_update_stats.)  Note that the
+ * index AM itself must keep track of the number of index tuples; we don't do
+ * so here because the AM might reject some of the tuples for its own reasons,
+ * such as being unable to store NULLs.
  *
  * A side effect is to set indexInfo->ii_BrokenHotChain to true if we detect
  * any potentially broken HOT chains.  Currently, we set this if there are
@@ -1898,6 +1954,106 @@ IndexBuildHeapScan(Relation heapRelation,
 }
 
 
+/*
+ * IndexCheckExclusion - verify that a new exclusion constraint is satisfied
+ *
+ * When creating an exclusion constraint, we first build the index normally
+ * and then rescan the heap to check for conflicts.  We assume that we only
+ * need to validate tuples that are live according to SnapshotNow, and that
+ * these were correctly indexed even in the presence of broken HOT chains.
+ * This should be OK since we are holding at least ShareLock on the table,
+ * meaning there can be no uncommitted updates from other transactions.
+ * (Note: that wouldn't necessarily work for system catalogs, since many
+ * operations release write lock early on the system catalogs.)
+ */
+static void
+IndexCheckExclusion(Relation heapRelation,
+					Relation indexRelation,
+					IndexInfo *indexInfo)
+{
+	HeapScanDesc scan;
+	HeapTuple	heapTuple;
+	Datum		values[INDEX_MAX_KEYS];
+	bool		isnull[INDEX_MAX_KEYS];
+	List	   *predicate;
+	TupleTableSlot *slot;
+	EState	   *estate;
+	ExprContext *econtext;
+
+	/*
+	 * Need an EState for evaluation of index expressions and partial-index
+	 * predicates.	Also a slot to hold the current tuple.
+	 */
+	estate = CreateExecutorState();
+	econtext = GetPerTupleExprContext(estate);
+	slot = MakeSingleTupleTableSlot(RelationGetDescr(heapRelation));
+
+	/* Arrange for econtext's scan tuple to be the tuple under test */
+	econtext->ecxt_scantuple = slot;
+
+	/* Set up execution state for predicate, if any. */
+	predicate = (List *)
+		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
+						estate);
+
+	/*
+	 * Scan all live tuples in the base relation.
+	 */
+	scan = heap_beginscan_strat(heapRelation,	/* relation */
+								SnapshotNow,	/* snapshot */
+								0,		/* number of keys */
+								NULL,	/* scan key */
+								true,	/* buffer access strategy OK */
+								true);	/* syncscan OK */
+
+	while ((heapTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+	{
+		CHECK_FOR_INTERRUPTS();
+
+		MemoryContextReset(econtext->ecxt_per_tuple_memory);
+
+		/* Set up for predicate or expression evaluation */
+		ExecStoreTuple(heapTuple, slot, InvalidBuffer, false);
+
+		/*
+		 * In a partial index, ignore tuples that don't satisfy the predicate.
+		 */
+		if (predicate != NIL)
+		{
+			if (!ExecQual(predicate, econtext, false))
+				continue;
+		}
+
+		/*
+		 * Extract index column values, including computing expressions.
+		 */
+		FormIndexDatum(indexInfo,
+					   slot,
+					   estate,
+					   values,
+					   isnull);
+
+		/*
+		 * Check that this tuple has no conflicts.
+		 */
+		check_exclusion_constraint(heapRelation,
+								   indexRelation, indexInfo,
+								   &(heapTuple->t_self), values, isnull,
+								   estate, true, false);
+	}
+
+	heap_endscan(scan);
+
+	ExecDropSingleTupleTableSlot(slot);
+
+	FreeExecutorState(estate);
+
+	/* These may have been pointing to the now-gone estate */
+	indexInfo->ii_ExpressionsState = NIL;
+	indexInfo->ii_PredicateState = NIL;
+}
+
+
 /*
  * validate_index - support code for concurrent index builds
  *
diff --git a/src/backend/catalog/indexing.c b/src/backend/catalog/indexing.c
index 65fe6d5efde41cf67d7bfa777afa43116a314bd5..cfe95cf2267fb34f7621023019bfd9767634c4db 100644
--- a/src/backend/catalog/indexing.c
+++ b/src/backend/catalog/indexing.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/catalog/indexing.c,v 1.118 2009/07/29 20:56:18 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/catalog/indexing.c,v 1.119 2009/12/07 05:22:21 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -30,7 +30,8 @@
  * In the current implementation, we share code for opening/closing the
  * indexes with execUtils.c.  But we do not use ExecInsertIndexTuples,
  * because we don't want to create an EState.  This implies that we
- * do not support partial or expressional indexes on system catalogs.
+ * do not support partial or expressional indexes on system catalogs,
+ * nor can we support generalized exclusion constraints.
  * This could be fixed with localized changes here if we wanted to pay
  * the extra overhead of building an EState.
  */
@@ -111,10 +112,12 @@ CatalogIndexInsert(CatalogIndexState indstate, HeapTuple heapTuple)
 
 		/*
 		 * Expressional and partial indexes on system catalogs are not
-		 * supported
+		 * supported, nor exclusion constraints, nor deferred uniqueness
 		 */
 		Assert(indexInfo->ii_Expressions == NIL);
 		Assert(indexInfo->ii_Predicate == NIL);
+		Assert(indexInfo->ii_ExclusionOps == NULL);
+		Assert(relationDescs[i]->rd_index->indimmediate);
 
 		/*
 		 * FormIndexDatum fills in its values and isnull parameters with the
diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql
index e973b0abc696da19d985cbbf891c50c508c7c57d..78532f52bb63fdd586fe005b1484984dfe629d5f 100644
--- a/src/backend/catalog/information_schema.sql
+++ b/src/backend/catalog/information_schema.sql
@@ -4,7 +4,7 @@
  *
  * Copyright (c) 2003-2009, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/backend/catalog/information_schema.sql,v 1.59 2009/12/05 21:43:35 petere Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/information_schema.sql,v 1.60 2009/12/07 05:22:21 tgl Exp $
  */
 
 /*
@@ -1666,6 +1666,7 @@ CREATE VIEW table_constraints AS
 
     WHERE nc.oid = c.connamespace AND nr.oid = r.relnamespace
           AND c.conrelid = r.oid
+          AND c.contype <> 'x'  -- ignore nonstandard exclusion constraints
           AND r.relkind = 'r'
           AND (NOT pg_is_other_temp_schema(nr.oid))
           AND (pg_has_role(r.relowner, 'USAGE')
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index d92bbf84cc069a0494bf68f94246ada36cdf279a..02cf5f1400bb5842479a38d840600b6dfff2b5cb 100644
--- a/src/backend/catalog/pg_constraint.c
+++ b/src/backend/catalog/pg_constraint.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/catalog/pg_constraint.c,v 1.49 2009/10/13 00:53:07 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/catalog/pg_constraint.c,v 1.50 2009/12/07 05:22:21 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -59,6 +59,7 @@ CreateConstraintEntry(const char *constraintName,
 					  char foreignUpdateType,
 					  char foreignDeleteType,
 					  char foreignMatchType,
+					  const Oid *exclOp,
 					  Node *conExpr,
 					  const char *conBin,
 					  const char *conSrc,
@@ -75,6 +76,7 @@ CreateConstraintEntry(const char *constraintName,
 	ArrayType  *conpfeqopArray;
 	ArrayType  *conppeqopArray;
 	ArrayType  *conffeqopArray;
+	ArrayType  *conexclopArray;
 	NameData	cname;
 	int			i;
 	ObjectAddress conobject;
@@ -130,6 +132,19 @@ CreateConstraintEntry(const char *constraintName,
 		conffeqopArray = NULL;
 	}
 
+	if (exclOp != NULL)
+	{
+		Datum	   *opdatums;
+
+		opdatums = (Datum *) palloc(constraintNKeys * sizeof(Datum));
+		for (i = 0; i < constraintNKeys; i++)
+			opdatums[i] = ObjectIdGetDatum(exclOp[i]);
+		conexclopArray = construct_array(opdatums, constraintNKeys,
+										 OIDOID, sizeof(Oid), true, 'i');
+	}
+	else
+		conexclopArray = NULL;
+
 	/* initialize nulls and values */
 	for (i = 0; i < Natts_pg_constraint; i++)
 	{
@@ -177,6 +192,11 @@ CreateConstraintEntry(const char *constraintName,
 	else
 		nulls[Anum_pg_constraint_conffeqop - 1] = true;
 
+	if (conexclopArray)
+		values[Anum_pg_constraint_conexclop - 1] = PointerGetDatum(conexclopArray);
+	else
+		nulls[Anum_pg_constraint_conexclop - 1] = true;
+
 	/*
 	 * initialize the binary form of the check constraint.
 	 */
@@ -321,6 +341,14 @@ CreateConstraintEntry(const char *constraintName,
 		}
 	}
 
+	/*
+	 * We don't bother to register dependencies on the exclusion operators
+	 * of an exclusion constraint.  We assume they are members of the opclass
+	 * supporting the index, so there's an indirect dependency via that.
+	 * (This would be pretty dicey for cross-type operators, but exclusion
+	 * operators can never be cross-type.)
+	 */
+
 	if (conExpr != NULL)
 	{
 		/*
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index dfc178724efdc7479ca5b25f35eafa6a3d836d89..05f3bb49691f83a4733bf359d5bae43a3dcf886c 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/catalog/toasting.c,v 1.20 2009/10/05 19:24:36 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/catalog/toasting.c,v 1.21 2009/12/07 05:22:21 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -239,6 +239,9 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 	indexInfo->ii_ExpressionsState = NIL;
 	indexInfo->ii_Predicate = NIL;
 	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_ExclusionOps = NULL;
+	indexInfo->ii_ExclusionProcs = NULL;
+	indexInfo->ii_ExclusionStrats = NULL;
 	indexInfo->ii_Unique = true;
 	indexInfo->ii_ReadyForInserts = true;
 	indexInfo->ii_Concurrent = false;
diff --git a/src/backend/commands/constraint.c b/src/backend/commands/constraint.c
index 42d4d4e1f9c4727f59d8c59fa1e0fef4a42e5db5..41adf871471edb1d304e45d5722c32a23d68729b 100644
--- a/src/backend/commands/constraint.c
+++ b/src/backend/commands/constraint.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/constraint.c,v 1.1 2009/07/29 20:56:18 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/constraint.c,v 1.2 2009/12/07 05:22:21 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -23,9 +23,12 @@
 /*
  * unique_key_recheck - trigger function to do a deferred uniqueness check.
  *
+ * This now also does deferred exclusion-constraint checks, so the name is
+ * somewhat historical.
+ *
  * This is invoked as an AFTER ROW trigger for both INSERT and UPDATE,
  * for any rows recorded as potentially violating a deferrable unique
- * constraint.
+ * or exclusion constraint.
  *
  * This may be an end-of-statement check, a commit-time check, or a
  * check triggered by a SET CONSTRAINTS command.
@@ -85,7 +88,7 @@ unique_key_recheck(PG_FUNCTION_ARGS)
 	 * because this trigger gets queued only in response to index insertions;
 	 * which means it does not get queued for HOT updates.  The row we are
 	 * called for might now be dead, but have a live HOT child, in which case
-	 * we still need to make the uniqueness check.  Therefore we have to use
+	 * we still need to make the check.  Therefore we have to use
 	 * heap_hot_search, not just HeapTupleSatisfiesVisibility as is done in
 	 * the comparable test in RI_FKey_check.
 	 *
@@ -123,9 +126,11 @@ unique_key_recheck(PG_FUNCTION_ARGS)
 
 	/*
 	 * Typically the index won't have expressions, but if it does we need
-	 * an EState to evaluate them.
+	 * an EState to evaluate them.  We need it for exclusion constraints
+	 * too, even if they are just on simple columns.
 	 */
-	if (indexInfo->ii_Expressions != NIL)
+	if (indexInfo->ii_Expressions != NIL ||
+		indexInfo->ii_ExclusionOps != NULL)
 	{
 		estate = CreateExecutorState();
 		econtext = GetPerTupleExprContext(estate);
@@ -141,19 +146,37 @@ unique_key_recheck(PG_FUNCTION_ARGS)
 	 * Note: if the index uses functions that are not as immutable as they
 	 * are supposed to be, this could produce an index tuple different from
 	 * the original.  The index AM can catch such errors by verifying that
-	 * it finds a matching index entry with the tuple's TID.
+	 * it finds a matching index entry with the tuple's TID.  For exclusion
+	 * constraints we check this in check_exclusion_constraint().
 	 */
 	FormIndexDatum(indexInfo, slot, estate, values, isnull);
 
 	/*
-	 * Now do the uniqueness check. This is not a real insert; it is a
-	 * check that the index entry that has already been inserted is unique.
+	 * Now do the appropriate check.
 	 */
-	index_insert(indexRel, values, isnull, &(new_row->t_self),
-				 trigdata->tg_relation, UNIQUE_CHECK_EXISTING);
+	if (indexInfo->ii_ExclusionOps == NULL)
+	{
+		/*
+		 * Note: this is not a real insert; it is a check that the index entry
+		 * that has already been inserted is unique.
+		 */
+		index_insert(indexRel, values, isnull, &(new_row->t_self),
+					 trigdata->tg_relation, UNIQUE_CHECK_EXISTING);
+	}
+	else
+	{
+		/*
+		 * For exclusion constraints we just do the normal check, but now
+		 * it's okay to throw error.
+		 */
+		check_exclusion_constraint(trigdata->tg_relation, indexRel, indexInfo,
+								   &(new_row->t_self), values, isnull,
+								   estate, false, false);
+	}
 
 	/*
-	 * If that worked, then this index entry is unique, and we are done.
+	 * If that worked, then this index entry is unique or non-excluded,
+	 * and we are done.
 	 */
 	if (estate != NULL)
 		FreeExecutorState(estate);
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 96272ab998d08a3187e338b1fdf0014494cd5106..00786442b3f35472332505634c27979d62cf3b4a 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/indexcmds.c,v 1.187 2009/07/29 20:56:18 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/indexcmds.c,v 1.188 2009/12/07 05:22:21 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -25,6 +25,7 @@
 #include "catalog/index.h"
 #include "catalog/indexing.h"
 #include "catalog/pg_opclass.h"
+#include "catalog/pg_opfamily.h"
 #include "catalog/pg_tablespace.h"
 #include "commands/dbcommands.h"
 #include "commands/defrem.h"
@@ -36,6 +37,7 @@
 #include "optimizer/clauses.h"
 #include "parser/parse_coerce.h"
 #include "parser/parse_func.h"
+#include "parser/parse_oper.h"
 #include "parser/parsetree.h"
 #include "storage/lmgr.h"
 #include "storage/proc.h"
@@ -58,6 +60,7 @@ static void ComputeIndexAttrs(IndexInfo *indexInfo,
 				  Oid *classOidP,
 				  int16 *colOptionP,
 				  List *attList,
+				  List *exclusionOpNames,
 				  Oid relId,
 				  char *accessMethodName, Oid accessMethodId,
 				  bool amcanorder,
@@ -83,6 +86,8 @@ static bool relationHasPrimaryKey(Relation rel);
  *		to index on.
  * 'predicate': the partial-index condition, or NULL if none.
  * 'options': reloptions from WITH (in list-of-DefElem form).
+ * 'exclusionOpNames': list of names of exclusion-constraint operators,
+ *		or NIL if not an exclusion constraint.
  * 'unique': make the index enforce uniqueness.
  * 'primary': mark the index as a primary key in the catalogs.
  * 'isconstraint': index is for a PRIMARY KEY or UNIQUE constraint,
@@ -106,6 +111,7 @@ DefineIndex(RangeVar *heapRelation,
 			List *attributeList,
 			Expr *predicate,
 			List *options,
+			List *exclusionOpNames,
 			bool unique,
 			bool primary,
 			bool isconstraint,
@@ -247,10 +253,21 @@ DefineIndex(RangeVar *heapRelation,
 	if (indexRelationName == NULL)
 	{
 		if (primary)
+		{
 			indexRelationName = ChooseRelationName(RelationGetRelationName(rel),
 												   NULL,
 												   "pkey",
 												   namespaceId);
+		}
+		else if (exclusionOpNames != NIL)
+		{
+			IndexElem  *iparam = (IndexElem *) linitial(attributeList);
+
+			indexRelationName = ChooseRelationName(RelationGetRelationName(rel),
+												   iparam->name,
+												   "exclusion",
+												   namespaceId);
+		}
 		else
 		{
 			IndexElem  *iparam = (IndexElem *) linitial(attributeList);
@@ -303,6 +320,11 @@ DefineIndex(RangeVar *heapRelation,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 		  errmsg("access method \"%s\" does not support multicolumn indexes",
 				 accessMethodName)));
+	if (exclusionOpNames != NIL && !OidIsValid(accessMethodForm->amgettuple))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("access method \"%s\" does not support exclusion constraints",
+						accessMethodName)));
 
 	amcanorder = accessMethodForm->amcanorder;
 	amoptions = accessMethodForm->amoptions;
@@ -418,6 +440,9 @@ DefineIndex(RangeVar *heapRelation,
 	indexInfo->ii_ExpressionsState = NIL;
 	indexInfo->ii_Predicate = make_ands_implicit(predicate);
 	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_ExclusionOps = NULL;
+	indexInfo->ii_ExclusionProcs = NULL;
+	indexInfo->ii_ExclusionStrats = NULL;
 	indexInfo->ii_Unique = unique;
 	/* In a concurrent build, mark it not-ready-for-inserts */
 	indexInfo->ii_ReadyForInserts = !concurrent;
@@ -427,7 +452,8 @@ DefineIndex(RangeVar *heapRelation,
 	classObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
 	coloptions = (int16 *) palloc(numberOfAttributes * sizeof(int16));
 	ComputeIndexAttrs(indexInfo, classObjectId, coloptions, attributeList,
-					  relationId, accessMethodName, accessMethodId,
+					  exclusionOpNames, relationId,
+					  accessMethodName, accessMethodId,
 					  amcanorder, isconstraint);
 
 	/*
@@ -435,11 +461,27 @@ DefineIndex(RangeVar *heapRelation,
 	 * error checks)
 	 */
 	if (isconstraint && !quiet)
+	{
+		const char *constraint_type;
+
+		if (primary)
+			constraint_type = "PRIMARY KEY";
+		else if (unique)
+			constraint_type = "UNIQUE";
+		else if (exclusionOpNames != NIL)
+			constraint_type = "EXCLUDE";
+		else
+		{
+			elog(ERROR, "unknown constraint type");
+			constraint_type = NULL;	/* keep compiler quiet */
+		}
+
 		ereport(NOTICE,
 		  (errmsg("%s %s will create implicit index \"%s\" for table \"%s\"",
 				  is_alter_table ? "ALTER TABLE / ADD" : "CREATE TABLE /",
-				  primary ? "PRIMARY KEY" : "UNIQUE",
+				  constraint_type,
 				  indexRelationName, RelationGetRelationName(rel))));
+	}
 
 	/* save lockrelid and locktag for below, then close rel */
 	heaprelid = rel->rd_lockInfo.lockRelId;
@@ -799,21 +841,38 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
 				  Oid *classOidP,
 				  int16 *colOptionP,
 				  List *attList,	/* list of IndexElem's */
+				  List *exclusionOpNames,
 				  Oid relId,
 				  char *accessMethodName,
 				  Oid accessMethodId,
 				  bool amcanorder,
 				  bool isconstraint)
 {
-	ListCell   *rest;
-	int			attn = 0;
+	ListCell   *nextExclOp;
+	ListCell   *lc;
+	int			attn;
+
+	/* Allocate space for exclusion operator info, if needed */
+	if (exclusionOpNames)
+	{
+		int		ncols = list_length(attList);
+
+		Assert(list_length(exclusionOpNames) == ncols);
+		indexInfo->ii_ExclusionOps = (Oid *) palloc(sizeof(Oid) * ncols);
+		indexInfo->ii_ExclusionProcs = (Oid *) palloc(sizeof(Oid) * ncols);
+		indexInfo->ii_ExclusionStrats = (uint16 *) palloc(sizeof(uint16) * ncols);
+		nextExclOp = list_head(exclusionOpNames);
+	}
+	else
+		nextExclOp = NULL;
 
 	/*
 	 * process attributeList
 	 */
-	foreach(rest, attList)
+	attn = 0;
+	foreach(lc, attList)
 	{
-		IndexElem  *attribute = (IndexElem *) lfirst(rest);
+		IndexElem  *attribute = (IndexElem *) lfirst(lc);
 		Oid			atttype;
 
 		/*
@@ -897,6 +956,71 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
 										  accessMethodName,
 										  accessMethodId);
 
+		/*
+		 * Identify the exclusion operator, if any.
+		 */
+		if (nextExclOp)
+		{
+			List   *opname = (List *) lfirst(nextExclOp);
+			Oid		opid;
+			Oid		opfamily;
+			int		strat;
+
+			/*
+			 * Find the operator --- it must accept the column datatype
+			 * without runtime coercion (but binary compatibility is OK)
+			 */
+			opid = compatible_oper_opid(opname, atttype, atttype, false);
+
+			/*
+			 * Only allow commutative operators to be used in exclusion
+			 * constraints. If X conflicts with Y, but Y does not conflict
+			 * with X, bad things will happen.
+			 */
+			if (get_commutator(opid) != opid)
+				ereport(ERROR,
+						(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+						 errmsg("operator %s is not commutative",
+								format_operator(opid)),
+						 errdetail("Only commutative operators can be used in exclusion constraints.")));
+
+			/*
+			 * Operator must be a member of the right opfamily, too
+			 */
+			opfamily = get_opclass_family(classOidP[attn]);
+			strat = get_op_opfamily_strategy(opid, opfamily);
+			if (strat == 0)
+			{
+				HeapTuple opftuple;
+				Form_pg_opfamily opfform;
+
+				/*
+				 * attribute->opclass might not explicitly name the opfamily,
+				 * so fetch the name of the selected opfamily for use in the
+				 * error message.
+				 */
+				opftuple = SearchSysCache(OPFAMILYOID,
+										  ObjectIdGetDatum(opfamily),
+										  0, 0, 0);
+				if (!HeapTupleIsValid(opftuple))
+					elog(ERROR, "cache lookup failed for opfamily %u",
+						 opfamily);
+				opfform = (Form_pg_opfamily) GETSTRUCT(opftuple);
+
+				ereport(ERROR,
+						(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+						 errmsg("operator %s is not a member of operator family \"%s\"",
+								format_operator(opid),
+								NameStr(opfform->opfname)),
+						 errdetail("The exclusion operator must be related to the index operator class for the constraint.")));
+			}
+
+			indexInfo->ii_ExclusionOps[attn] = opid;
+			indexInfo->ii_ExclusionProcs[attn] = get_opcode(opid);
+			indexInfo->ii_ExclusionStrats[attn] = strat;
+			nextExclOp = lnext(nextExclOp);
+		}
+
 		/*
 		 * Set up the per-column options (indoption field).  For now, this is
 		 * zero for any un-ordered index, while ordered indexes have DESC and
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 1de1950453158afca07f94464df2333cf69e7ee6..d10a7490b748b77888b451bea702d6ca16982403 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.306 2009/11/20 20:38:10 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.307 2009/12/07 05:22:21 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -4603,6 +4603,7 @@ ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
 				stmt->indexParams,		/* parameters */
 				(Expr *) stmt->whereClause,
 				stmt->options,
+				stmt->excludeOpNames,
 				stmt->unique,
 				stmt->primary,
 				stmt->isconstraint,
@@ -5035,6 +5036,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
 									  fkconstraint->fk_upd_action,
 									  fkconstraint->fk_del_action,
 									  fkconstraint->fk_matchtype,
+									  NULL,		/* no exclusion constraint */
 									  NULL,		/* no check constraint */
 									  NULL,
 									  NULL,
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 4f997a0dce1a7e01a12c4f8ccc90026cd1437ff0..54d8220cb37a59d241879aad0b20de63542e6333 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.138 2009/10/08 02:39:19 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.139 2009/12/07 05:22:21 tgl Exp $
  *
  * DESCRIPTION
  *	  The "DefineFoo" routines take the parse tree and pick out the
@@ -984,6 +984,12 @@ DefineDomain(CreateDomainStmt *stmt)
 				errmsg("primary key constraints not possible for domains")));
 				break;
 
+			case CONSTR_EXCLUSION:
+				ereport(ERROR,
+						(errcode(ERRCODE_SYNTAX_ERROR),
+					 errmsg("exclusion constraints not possible for domains")));
+				break;
+
 			case CONSTR_FOREIGN:
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
@@ -1868,6 +1874,12 @@ AlterDomainAddConstraint(List *names, Node *newConstraint)
 				errmsg("primary key constraints not possible for domains")));
 			break;
 
+		case CONSTR_EXCLUSION:
+			ereport(ERROR,
+					(errcode(ERRCODE_SYNTAX_ERROR),
+					 errmsg("exclusion constraints not possible for domains")));
+			break;
+
 		case CONSTR_FOREIGN:
 			ereport(ERROR,
 					(errcode(ERRCODE_SYNTAX_ERROR),
@@ -2297,9 +2309,10 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
 						  ' ',
 						  ' ',
 						  ' ',
-						  expr, /* Tree form check constraint */
-						  ccbin,	/* Binary form check constraint */
-						  ccsrc,	/* Source form check constraint */
+						  NULL,	/* not an exclusion constraint */
+						  expr, /* Tree form of check constraint */
+						  ccbin,	/* Binary form of check constraint */
+						  ccsrc,	/* Source form of check constraint */
 						  true, /* is local */
 						  0);	/* inhcount */
 
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index eeee7654e0e47aad0594f1850dad4265dcdf465c..83b8c18e8eb4f774ac9f44691e94d2a1e570cb13 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -13,7 +13,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.396 2009/11/16 21:32:06 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.397 2009/12/07 05:22:21 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -763,7 +763,8 @@ vac_update_relstats(Relation relation,
 
 	/*
 	 * If we have discovered that there are no indexes, then there's no
-	 * primary key either.	This could be done more thoroughly...
+	 * primary key either, nor any exclusion constraints.  This could be done
+	 * more thoroughly...
 	 */
 	if (!hasindex)
 	{
@@ -772,6 +773,11 @@ vac_update_relstats(Relation relation,
 			pgcform->relhaspkey = false;
 			dirty = true;
 		}
+		if (pgcform->relhasexclusion && pgcform->relkind != RELKIND_INDEX)
+		{
+			pgcform->relhasexclusion = false;
+			dirty = true;
+		}
 	}
 
 	/* We also clear relhasrules and relhastriggers if needed */
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index e1aa8ce8ac9dd239fea70a9e1dd8d24d9822af34..ac993465a5108854c238e164789139b6ba660dcf 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.166 2009/11/20 20:38:10 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.167 2009/12/07 05:22:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -44,16 +44,22 @@
 
 #include "access/genam.h"
 #include "access/heapam.h"
+#include "access/relscan.h"
+#include "access/transam.h"
 #include "catalog/index.h"
 #include "executor/execdebug.h"
 #include "nodes/nodeFuncs.h"
 #include "parser/parsetree.h"
+#include "storage/lmgr.h"
 #include "utils/memutils.h"
 #include "utils/relcache.h"
 #include "utils/tqual.h"
 
 
 static bool get_last_attnums(Node *node, ProjectionInfo *projInfo);
+static bool index_recheck_constraint(Relation index, Oid *constr_procs,
+						 Datum *existing_values, bool *existing_isnull,
+						 Datum *new_values);
 static void ShutdownExprContext(ExprContext *econtext, bool isCommit);
 
 
@@ -959,8 +965,8 @@ ExecCloseIndices(ResultRelInfo *resultRelInfo)
  *		doesn't provide the functionality needed by the
  *		executor.. -cim 9/27/89
  *
- *		This returns a list of OIDs for any unique indexes
- *		whose constraint check was deferred and which had
+ *		This returns a list of index OIDs for any unique or exclusion
+ *		constraints that are deferred and that had
  *		potential (unconfirmed) conflicts.
  *
  *		CAUTION: this must not be called for a HOT update.
@@ -1011,7 +1017,7 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
 		Relation	indexRelation = relationDescs[i];
 		IndexInfo  *indexInfo;
 		IndexUniqueCheck checkUnique;
-		bool		isUnique;
+		bool		satisfiesConstraint;
 
 		if (indexRelation == NULL)
 			continue;
@@ -1056,7 +1062,7 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
 					   isnull);
 
 		/*
-		 * The index AM does the rest, including uniqueness checking.
+		 * The index AM does the actual insertion, plus uniqueness checking.
 		 *
 		 * For an immediate-mode unique index, we just tell the index AM to
 		 * throw error if not unique.
@@ -1076,7 +1082,7 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
 		else
 			checkUnique = UNIQUE_CHECK_PARTIAL;
 
-		isUnique =
+		satisfiesConstraint =
 			index_insert(indexRelation,	/* index relation */
 						 values,		/* array of index Datums */
 						 isnull,		/* null flags */
@@ -1084,11 +1090,36 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
 						 heapRelation,	/* heap relation */
 						 checkUnique);	/* type of uniqueness check to do */
 
-		if (checkUnique == UNIQUE_CHECK_PARTIAL && !isUnique)
+		/*
+		 * If the index has an associated exclusion constraint, check that.
+		 * This is simpler than the process for uniqueness checks since we
+		 * always insert first and then check.  If the constraint is deferred,
+		 * we check now anyway, but don't throw error on violation; instead
+		 * we'll queue a recheck event.
+		 *
+		 * An index for an exclusion constraint can't also be UNIQUE (not an
+		 * essential property, we just don't allow it in the grammar), so no
+		 * need to preserve the prior state of satisfiesConstraint.
+		 */
+		if (indexInfo->ii_ExclusionOps != NULL)
+		{
+			bool errorOK = !indexRelation->rd_index->indimmediate;
+
+			satisfiesConstraint =
+				check_exclusion_constraint(heapRelation,
+										   indexRelation, indexInfo,
+										   tupleid, values, isnull,
+										   estate, false, errorOK);
+		}
+
+		if ((checkUnique == UNIQUE_CHECK_PARTIAL ||
+			 indexInfo->ii_ExclusionOps != NULL) &&
+			!satisfiesConstraint)
 		{
 			/*
-			 * The tuple potentially violates the uniqueness constraint,
-			 * so make a note of the index so that we can re-check it later.
+			 * The tuple potentially violates the uniqueness or exclusion
+			 * constraint, so make a note of the index so that we can re-check
+			 * it later.
 			 */
 			result = lappend_oid(result, RelationGetRelid(indexRelation));
 		}
@@ -1097,6 +1128,221 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
 	return result;
 }
 
+/*
+ * Check for violation of an exclusion constraint
+ *
+ * heap: the table containing the new tuple
+ * index: the index supporting the exclusion constraint
+ * indexInfo: info about the index, including the exclusion properties
+ * tupleid: heap TID of the new tuple we have just inserted
+ * values, isnull: the *index* column values computed for the new tuple
+ * estate: an EState we can do evaluation in
+ * newIndex: if true, we are trying to build a new index (this affects
+ *		only the wording of error messages)
+ * errorOK: if true, don't throw error for violation
+ *
+ * Returns true if OK, false if actual or potential violation
+ *
+ * When errorOK is true, we report violation without waiting to see if any
+ * concurrent transaction has committed or not; so the violation is only
+ * potential, and the caller must recheck sometime later.  This behavior
+ * is convenient for deferred exclusion checks; we need not bother queuing
+ * a deferred event if there is definitely no conflict at insertion time.
+ *
+ * When errorOK is false, we'll throw error on violation, so a false result
+ * is impossible.
+ */
+bool
+check_exclusion_constraint(Relation heap, Relation index, IndexInfo *indexInfo,
+						   ItemPointer tupleid, Datum *values, bool *isnull,
+						   EState *estate, bool newIndex, bool errorOK)
+{
+	Oid			*constr_procs = indexInfo->ii_ExclusionProcs;
+	uint16		*constr_strats = indexInfo->ii_ExclusionStrats;
+	int			 index_natts = index->rd_index->indnatts;
+	IndexScanDesc	index_scan;
+	HeapTuple		tup;
+	ScanKeyData		scankeys[INDEX_MAX_KEYS];
+	SnapshotData	DirtySnapshot;
+	int				i;
+	bool			conflict;
+	bool			found_self;
+	TupleTableSlot *existing_slot;
+
+	/*
+	 * If any of the input values are NULL, the constraint check is assumed
+	 * to pass (i.e., we assume the operators are strict).
+	 */
+	for (i = 0; i < index_natts; i++)
+	{
+		if (isnull[i])
+			return true;
+	}
+
+	/*
+	 * Search the tuples that are in the index for any violations,
+	 * including tuples that aren't visible yet.
+	 */
+	InitDirtySnapshot(DirtySnapshot);
+
+	for (i = 0; i < index_natts; i++)
+	{
+		ScanKeyInit(&scankeys[i],
+					i + 1,
+					constr_strats[i],
+					constr_procs[i],
+					values[i]);
+	}
+
+	/* Need a TupleTableSlot to put existing tuples in */
+	existing_slot = MakeSingleTupleTableSlot(RelationGetDescr(heap));
+
+	/*
+	 * May have to restart scan from this point if a potential
+	 * conflict is found.
+	 */
+retry:
+	conflict = false;
+	found_self = false;
+	index_scan = index_beginscan(heap, index, &DirtySnapshot,
+								 index_natts, scankeys);
+
+	while ((tup = index_getnext(index_scan,
+								ForwardScanDirection)) != NULL)
+	{
+		TransactionId	 xwait;
+		Datum		existing_values[INDEX_MAX_KEYS];
+		bool		existing_isnull[INDEX_MAX_KEYS];
+		char		*error_new;
+		char		*error_existing;
+
+		/*
+		 * Ignore the entry for the tuple we're trying to check.
+		 */
+		if (ItemPointerEquals(tupleid, &tup->t_self))
+		{
+			if (found_self)		/* should not happen */
+				elog(ERROR, "found self tuple multiple times in index \"%s\"",
+					 RelationGetRelationName(index));
+			found_self = true;
+			continue;
+		}
+
+		/*
+		 * Extract the index column values and isnull flags from the existing
+		 * tuple.
+		 */
+		ExecStoreTuple(tup,	existing_slot, InvalidBuffer, false);
+		FormIndexDatum(indexInfo, existing_slot, estate,
+					   existing_values, existing_isnull);
+
+		/* If lossy indexscan, must recheck the condition */
+		if (index_scan->xs_recheck)
+		{
+			if (!index_recheck_constraint(index,
+										  constr_procs,
+										  existing_values,
+										  existing_isnull,
+										  values))
+				continue; /* tuple doesn't actually match, so no conflict */
+		}
+
+		/*
+		 * At this point we have either a conflict or a potential conflict.
+		 * If we're not supposed to raise error, just return the fact of the
+		 * potential conflict without waiting to see if it's real.
+		 */
+		if (errorOK)
+		{
+			conflict = true;
+			break;
+		}
+
+		/*
+		 * If an in-progress transaction is affecting the visibility of this
+		 * tuple, we need to wait for it to complete and then recheck.  For
+		 * simplicity we do rechecking by just restarting the whole scan ---
+		 * this case probably doesn't happen often enough to be worth trying
+		 * harder, and anyway we don't want to hold any index internal locks
+		 * while waiting.
+		 */
+		xwait = TransactionIdIsValid(DirtySnapshot.xmin) ?
+			DirtySnapshot.xmin : DirtySnapshot.xmax;
+
+		if (TransactionIdIsValid(xwait))
+		{
+			index_endscan(index_scan);
+			XactLockTableWait(xwait);
+			goto retry;
+		}
+
+		/*
+		 * We have a definite conflict.  Report it.
+		 */
+		error_new = BuildIndexValueDescription(index, values, isnull);
+		error_existing = BuildIndexValueDescription(index, existing_values,
+													existing_isnull);
+		if (newIndex)
+			ereport(ERROR,
+					(errcode(ERRCODE_EXCLUSION_VIOLATION),
+					 errmsg("could not create exclusion constraint \"%s\"",
+							RelationGetRelationName(index)),
+					 errdetail("Key %s conflicts with key %s.",
+							   error_new, error_existing)));
+		else
+			ereport(ERROR,
+					(errcode(ERRCODE_EXCLUSION_VIOLATION),
+					 errmsg("conflicting key value violates exclusion constraint \"%s\"",
+							RelationGetRelationName(index)),
+					 errdetail("Key %s conflicts with existing key %s.",
+							   error_new, error_existing)));
+	}
+
+	index_endscan(index_scan);
+
+	/*
+	 * We should have found our tuple in the index, unless we exited the
+	 * loop early because of conflict.  Complain if not.
+	 */
+	if (!found_self && !conflict)
+		ereport(ERROR,
+				(errcode(ERRCODE_INTERNAL_ERROR),
+				 errmsg("failed to re-find tuple within index \"%s\"",
+						RelationGetRelationName(index)),
+				 errhint("This may be because of a non-immutable index expression.")));
+
+	ExecDropSingleTupleTableSlot(existing_slot);
+
+	return !conflict;
+}
+
+/*
+ * Check existing tuple's index values to see if it really matches the
+ * exclusion condition against the new_values.  Returns true if conflict.
+ */
+static bool
+index_recheck_constraint(Relation index, Oid *constr_procs,
+						 Datum *existing_values, bool *existing_isnull,
+						 Datum *new_values)
+{
+	int			index_natts = index->rd_index->indnatts;
+	int			i;
+
+	for (i = 0; i < index_natts; i++)
+	{
+		/* Assume the exclusion operators are strict */
+		if (existing_isnull[i])
+			return false;
+
+		if (!DatumGetBool(OidFunctionCall2(constr_procs[i],
+										   existing_values[i],
+										   new_values[i])))
+			return false;
+	}
+
+	return true;
+}
+
 /*
  * UpdateChangedParamSet
  *		Add changed parameters to a plan node's chgParam set
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 35656d86566ea3892b22bfebf482d615eaba6e75..c0bb01f1414c3e3203b0f20c625d4a4d044971df 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.452 2009/11/20 20:38:10 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.453 2009/12/07 05:22:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2157,8 +2157,11 @@ _copyConstraint(Constraint *from)
 	COPY_NODE_FIELD(raw_expr);
 	COPY_STRING_FIELD(cooked_expr);
 	COPY_NODE_FIELD(keys);
+	COPY_NODE_FIELD(exclusions);
 	COPY_NODE_FIELD(options);
 	COPY_STRING_FIELD(indexspace);
+	COPY_STRING_FIELD(access_method);
+	COPY_NODE_FIELD(where_clause);
 	COPY_NODE_FIELD(pktable);
 	COPY_NODE_FIELD(fk_attrs);
 	COPY_NODE_FIELD(pk_attrs);
@@ -2595,6 +2598,7 @@ _copyIndexStmt(IndexStmt *from)
 	COPY_NODE_FIELD(indexParams);
 	COPY_NODE_FIELD(options);
 	COPY_NODE_FIELD(whereClause);
+	COPY_NODE_FIELD(excludeOpNames);
 	COPY_SCALAR_FIELD(unique);
 	COPY_SCALAR_FIELD(primary);
 	COPY_SCALAR_FIELD(isconstraint);
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index eeda5799ee35f7cebbd51a9bdb9b6068a4d70a61..7a8bedf1cb960b4c6abd104fbd449929e39c43de 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -22,7 +22,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.374 2009/11/20 20:38:10 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.375 2009/12/07 05:22:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1179,6 +1179,7 @@ _equalIndexStmt(IndexStmt *a, IndexStmt *b)
 	COMPARE_NODE_FIELD(indexParams);
 	COMPARE_NODE_FIELD(options);
 	COMPARE_NODE_FIELD(whereClause);
+	COMPARE_NODE_FIELD(excludeOpNames);
 	COMPARE_SCALAR_FIELD(unique);
 	COMPARE_SCALAR_FIELD(primary);
 	COMPARE_SCALAR_FIELD(isconstraint);
@@ -2103,8 +2104,11 @@ _equalConstraint(Constraint *a, Constraint *b)
 	COMPARE_NODE_FIELD(raw_expr);
 	COMPARE_STRING_FIELD(cooked_expr);
 	COMPARE_NODE_FIELD(keys);
+	COMPARE_NODE_FIELD(exclusions);
 	COMPARE_NODE_FIELD(options);
 	COMPARE_STRING_FIELD(indexspace);
+	COMPARE_STRING_FIELD(access_method);
+	COMPARE_NODE_FIELD(where_clause);
 	COMPARE_NODE_FIELD(pktable);
 	COMPARE_NODE_FIELD(fk_attrs);
 	COMPARE_NODE_FIELD(pk_attrs);
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 690967595044daaa82900aa468cef1a8d2012313..80a2fa76407ec1556804d7b10c6046888c326cb4 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.373 2009/11/28 00:46:18 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.374 2009/12/07 05:22:22 tgl Exp $
  *
  * NOTES
  *	  Every node type that can appear in stored rules' parsetrees *must*
@@ -1798,6 +1798,7 @@ _outIndexStmt(StringInfo str, IndexStmt *node)
 	WRITE_NODE_FIELD(indexParams);
 	WRITE_NODE_FIELD(options);
 	WRITE_NODE_FIELD(whereClause);
+	WRITE_NODE_FIELD(excludeOpNames);
 	WRITE_BOOL_FIELD(unique);
 	WRITE_BOOL_FIELD(primary);
 	WRITE_BOOL_FIELD(isconstraint);
@@ -2378,6 +2379,7 @@ _outConstraint(StringInfo str, Constraint *node)
 			WRITE_NODE_FIELD(keys);
 			WRITE_NODE_FIELD(options);
 			WRITE_STRING_FIELD(indexspace);
+			/* access_method and where_clause not currently used */
 			break;
 
 		case CONSTR_UNIQUE:
@@ -2385,6 +2387,16 @@ _outConstraint(StringInfo str, Constraint *node)
 			WRITE_NODE_FIELD(keys);
 			WRITE_NODE_FIELD(options);
 			WRITE_STRING_FIELD(indexspace);
+			/* access_method and where_clause not currently used */
+			break;
+
+		case CONSTR_EXCLUSION:
+			appendStringInfo(str, "EXCLUSION");
+			WRITE_NODE_FIELD(exclusions);
+			WRITE_NODE_FIELD(options);
+			WRITE_STRING_FIELD(indexspace);
+			WRITE_STRING_FIELD(access_method);
+			WRITE_NODE_FIELD(where_clause);
 			break;
 
 		case CONSTR_FOREIGN:
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index cab6d915c8a13cfb2f744e886112e4799393a1bd..8dca257360575558d73f985d72767c6bdd418fbb 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.694 2009/11/20 20:38:10 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.695 2009/12/07 05:22:22 tgl Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -352,6 +352,8 @@ static TypeName *TableFuncTypeName(List *columns);
 %type <node>	def_arg columnElem where_clause where_or_current_clause
 				a_expr b_expr c_expr func_expr AexprConst indirection_el
 				columnref in_expr having_clause func_table array_expr
+				ExclusionWhereClause
+%type <list>	ExclusionConstraintList ExclusionConstraintElem
 %type <list>	func_arg_list
 %type <node>	func_arg_expr
 %type <list>	row type_list array_expr_list
@@ -475,7 +477,7 @@ static TypeName *TableFuncTypeName(List *columns);
 	DICTIONARY DISABLE_P DISCARD DISTINCT DO DOCUMENT_P DOMAIN_P DOUBLE_P DROP
 
 	EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EXCEPT
-	EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN EXTERNAL EXTRACT
+	EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN EXTERNAL EXTRACT
 
 	FALSE_P FAMILY FETCH FIRST_P FLOAT_P FOLLOWING FOR FORCE FOREIGN FORWARD
 	FREEZE FROM FULL FUNCTION FUNCTIONS
@@ -1591,8 +1593,16 @@ alter_table_cmds:
 		;
 
 alter_table_cmd:
-			/* ALTER TABLE <name> ADD [COLUMN] <coldef> */
-			ADD_P opt_column columnDef
+			/* ALTER TABLE <name> ADD <coldef> */
+			ADD_P columnDef
+				{
+					AlterTableCmd *n = makeNode(AlterTableCmd);
+					n->subtype = AT_AddColumn;
+					n->def = $2;
+					$$ = (Node *)n;
+				}
+			/* ALTER TABLE <name> ADD COLUMN <coldef> */
+			| ADD_P COLUMN columnDef
 				{
 					AlterTableCmd *n = makeNode(AlterTableCmd);
 					n->subtype = AT_AddColumn;
@@ -2487,6 +2497,22 @@ ConstraintElem:
 					n->initdeferred = ($8 & 2) != 0;
 					$$ = (Node *)n;
 				}
+			| EXCLUDE access_method_clause '(' ExclusionConstraintList ')'
+				opt_definition OptConsTableSpace ExclusionWhereClause
+				ConstraintAttributeSpec
+				{
+					Constraint *n = makeNode(Constraint);
+					n->contype = CONSTR_EXCLUSION;
+					n->location = @1;
+					n->access_method	= $2;
+					n->exclusions		= $4;
+					n->options			= $6;
+					n->indexspace		= $7;
+					n->where_clause		= $8;
+					n->deferrable		= ($9 & 1) != 0;
+					n->initdeferred		= ($9 & 2) != 0;
+					$$ = (Node *)n;
+				}
 			| FOREIGN KEY '(' columnList ')' REFERENCES qualified_name
 				opt_column_list key_match key_actions ConstraintAttributeSpec
 				{
@@ -2544,6 +2570,28 @@ key_match:  MATCH FULL
 			}
 		;
 
+ExclusionConstraintList:
+			ExclusionConstraintElem					{ $$ = list_make1($1); }
+			| ExclusionConstraintList ',' ExclusionConstraintElem
+													{ $$ = lappend($1, $3); }
+		;
+
+ExclusionConstraintElem: index_elem WITH any_operator
+			{
+				$$ = list_make2($1, $3);
+			}
+			/* allow OPERATOR() decoration for the benefit of ruleutils.c */
+			| index_elem WITH OPERATOR '(' any_operator ')'
+			{
+				$$ = list_make2($1, $5);
+			}
+		;
+
+ExclusionWhereClause:
+			WHERE '(' a_expr ')'					{ $$ = $3; }
+			| /*EMPTY*/								{ $$ = NULL; }
+		;
+
 /*
  * We combine the update and delete actions into one value temporarily
  * for simplicity of parsing, and then break them down again in the
@@ -10619,6 +10667,7 @@ unreserved_keyword:
 			| ENCRYPTED
 			| ENUM_P
 			| ESCAPE
+			| EXCLUDE
 			| EXCLUDING
 			| EXCLUSIVE
 			| EXECUTE
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 18f7e5c556e506249c85e20dc68036be8c4dc9c8..f21fcb610443580a185b5635eaf8a8e1a63bfbb5 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -19,7 +19,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *	$PostgreSQL: pgsql/src/backend/parser/parse_utilcmd.c,v 2.30 2009/11/13 23:49:23 tgl Exp $
+ *	$PostgreSQL: pgsql/src/backend/parser/parse_utilcmd.c,v 2.31 2009/12/07 05:22:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -35,6 +35,7 @@
 #include "catalog/namespace.h"
 #include "catalog/pg_constraint.h"
 #include "catalog/pg_opclass.h"
+#include "catalog/pg_operator.h"
 #include "catalog/pg_type.h"
 #include "commands/comment.h"
 #include "commands/defrem.h"
@@ -456,6 +457,10 @@ transformColumnDefinition(ParseState *pstate, CreateStmtContext *cxt,
 				saw_default = true;
 				break;
 
+			case CONSTR_CHECK:
+				cxt->ckconstraints = lappend(cxt->ckconstraints, constraint);
+				break;
+
 			case CONSTR_PRIMARY:
 			case CONSTR_UNIQUE:
 				if (constraint->keys == NIL)
@@ -463,8 +468,9 @@ transformColumnDefinition(ParseState *pstate, CreateStmtContext *cxt,
 				cxt->ixconstraints = lappend(cxt->ixconstraints, constraint);
 				break;
 
-			case CONSTR_CHECK:
-				cxt->ckconstraints = lappend(cxt->ckconstraints, constraint);
+			case CONSTR_EXCLUSION:
+				/* grammar does not allow EXCLUDE as a column constraint */
+				elog(ERROR, "column exclusion constraints are not supported");
 				break;
 
 			case CONSTR_FOREIGN:
@@ -503,6 +509,7 @@ transformTableConstraint(ParseState *pstate, CreateStmtContext *cxt,
 	{
 		case CONSTR_PRIMARY:
 		case CONSTR_UNIQUE:
+		case CONSTR_EXCLUSION:
 			cxt->ixconstraints = lappend(cxt->ixconstraints, constraint);
 			break;
 
@@ -814,7 +821,7 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
 /*
  * chooseIndexName
  *
- * Set name to unnamed index. See also the same logic in DefineIndex.
+ * Set name for unnamed index. See also the same logic in DefineIndex.
  */
 static char *
 chooseIndexName(const RangeVar *relation, IndexStmt *index_stmt)
@@ -828,6 +835,13 @@ chooseIndexName(const RangeVar *relation, IndexStmt *index_stmt)
 		return ChooseRelationName(relation->relname, NULL, 
 								  "pkey", namespaceId);
 	}
+	else if (index_stmt->excludeOpNames != NIL)
+	{
+		IndexElem  *iparam = (IndexElem *) linitial(index_stmt->indexParams);
+
+		return ChooseRelationName(relation->relname, iparam->name,
+								  "exclusion", namespaceId);
+	}
 	else
 	{
 		IndexElem  *iparam = (IndexElem *) linitial(index_stmt->indexParams);
@@ -880,7 +894,7 @@ generateClonedIndexStmt(CreateStmtContext *cxt, Relation source_idx,
 	/* Fetch pg_am tuple for source index from relcache entry */
 	amrec = source_idx->rd_am;
 
-	/* Must get indclass the hard way, since it's not stored in relcache */
+	/* Extract indclass from the pg_index tuple */
 	datum = SysCacheGetAttr(INDEXRELID, ht_idx,
 							Anum_pg_index_indclass, &isnull);
 	Assert(!isnull);
@@ -905,12 +919,12 @@ generateClonedIndexStmt(CreateStmtContext *cxt, Relation source_idx,
 	index->idxname = NULL;
 
 	/*
-	 * If the index is marked PRIMARY, it's certainly from a constraint; else,
-	 * if it's not marked UNIQUE, it certainly isn't.  If it is or might be
-	 * from a constraint, we have to fetch the constraint to check for
-	 * deferrability attributes.
+	 * If the index is marked PRIMARY or has an exclusion condition, it's
+	 * certainly from a constraint; else, if it's not marked UNIQUE, it
+	 * certainly isn't.  If it is or might be from a constraint, we have to
+	 * fetch the pg_constraint record.
 	 */
-	if (index->primary || index->unique)
+	if (index->primary || index->unique || idxrelrec->relhasexclusion)
 	{
 		Oid		constraintId = get_index_constraint(source_relid);
 
@@ -931,6 +945,53 @@ generateClonedIndexStmt(CreateStmtContext *cxt, Relation source_idx,
 			index->deferrable = conrec->condeferrable;
 			index->initdeferred = conrec->condeferred;
 
+			/* If it's an exclusion constraint, we need the operator names */
+			if (idxrelrec->relhasexclusion)
+			{
+				Datum  *elems;
+				int		nElems;
+				int		i;
+
+				Assert(conrec->contype == CONSTRAINT_EXCLUSION);
+				/* Extract operator OIDs from the pg_constraint tuple */
+				datum = SysCacheGetAttr(CONSTROID, ht_constr,
+										Anum_pg_constraint_conexclop,
+										&isnull);
+				if (isnull)
+					elog(ERROR, "null conexclop for constraint %u",
+						 constraintId);
+
+				deconstruct_array(DatumGetArrayTypeP(datum),
+								  OIDOID, sizeof(Oid), true, 'i',
+								  &elems, NULL, &nElems);
+
+				for (i = 0; i < nElems; i++)
+				{
+					Oid			operid = DatumGetObjectId(elems[i]);
+					HeapTuple	opertup;
+					Form_pg_operator operform;
+					char	   *oprname;
+					char	   *nspname;
+					List	   *namelist;
+
+					opertup = SearchSysCache(OPEROID,
+											 ObjectIdGetDatum(operid),
+											 0, 0, 0);
+					if (!HeapTupleIsValid(opertup))
+						elog(ERROR, "cache lookup failed for operator %u",
+							 operid);
+					operform = (Form_pg_operator) GETSTRUCT(opertup);
+					oprname = pstrdup(NameStr(operform->oprname));
+					/* For simplicity we always schema-qualify the op name */
+					nspname = get_namespace_name(operform->oprnamespace);
+					namelist = list_make2(makeString(nspname),
+										  makeString(oprname));
+					index->excludeOpNames = lappend(index->excludeOpNames,
+													namelist);
+					ReleaseSysCache(opertup);
+				}
+			}
+
 			ReleaseSysCache(ht_constr);
 		}
 		else
@@ -1087,7 +1148,7 @@ get_opclass(Oid opclass, Oid actual_datatype)
 
 /*
  * transformIndexConstraints
- *		Handle UNIQUE and PRIMARY KEY constraints, which create indexes.
+ *		Handle UNIQUE, PRIMARY KEY, EXCLUDE constraints, which create indexes.
  *		We also merge in any index definitions arising from
  *		LIKE ... INCLUDING INDEXES.
  */
@@ -1100,8 +1161,9 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt)
 
 	/*
 	 * Run through the constraints that need to generate an index. For PRIMARY
-	 * KEY, mark each column as NOT NULL and create an index. For UNIQUE,
-	 * create an index as for PRIMARY KEY, but do not insist on NOT NULL.
+	 * KEY, mark each column as NOT NULL and create an index. For UNIQUE or
+	 * EXCLUDE, create an index as for PRIMARY KEY, but do not insist on NOT
+	 * NULL.
 	 */
 	foreach(lc, cxt->ixconstraints)
 	{
@@ -1109,7 +1171,8 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt)
 
 		Assert(IsA(constraint, Constraint));
 		Assert(constraint->contype == CONSTR_PRIMARY ||
-			   constraint->contype == CONSTR_UNIQUE);
+			   constraint->contype == CONSTR_UNIQUE ||
+			   constraint->contype == CONSTR_EXCLUSION);
 
 		index = transformIndexConstraint(constraint, cxt);
 
@@ -1167,6 +1230,7 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt)
 
 			if (equal(index->indexParams, priorindex->indexParams) &&
 				equal(index->whereClause, priorindex->whereClause) &&
+				equal(index->excludeOpNames, priorindex->excludeOpNames) &&
 				strcmp(index->accessMethod, priorindex->accessMethod) == 0 &&
 				index->deferrable == priorindex->deferrable &&
 				index->initdeferred == priorindex->initdeferred)
@@ -1193,19 +1257,18 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt)
 
 /*
  * transformIndexConstraint
- *		Transform one UNIQUE or PRIMARY KEY constraint for
+ *		Transform one UNIQUE, PRIMARY KEY, or EXCLUDE constraint for
  *		transformIndexConstraints.
  */
 static IndexStmt *
 transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
 {
 	IndexStmt  *index;
-	ListCell   *keys;
-	IndexElem  *iparam;
+	ListCell   *lc;
 
 	index = makeNode(IndexStmt);
 
-	index->unique = true;
+	index->unique = (constraint->contype != CONSTR_EXCLUSION);
 	index->primary = (constraint->contype == CONSTR_PRIMARY);
 	if (index->primary)
 	{
@@ -1231,25 +1294,55 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
 		index->idxname = NULL;	/* DefineIndex will choose name */
 
 	index->relation = cxt->relation;
-	index->accessMethod = DEFAULT_INDEX_TYPE;
+	index->accessMethod = constraint->access_method ? constraint->access_method : DEFAULT_INDEX_TYPE;
 	index->options = constraint->options;
 	index->tableSpace = constraint->indexspace;
+	index->whereClause = constraint->where_clause;
 	index->indexParams = NIL;
-	index->whereClause = NULL;
+	index->excludeOpNames = NIL;
 	index->concurrent = false;
 
 	/*
+	 * If it's an EXCLUDE constraint, the grammar returns a list of pairs
+	 * of IndexElems and operator names.  We have to break that apart into
+	 * separate lists.
+	 */
+	if (constraint->contype == CONSTR_EXCLUSION)
+	{
+		foreach(lc, constraint->exclusions)
+		{
+			List	*pair = (List *) lfirst(lc);
+			IndexElem *elem;
+			List   *opname;
+
+			Assert(list_length(pair) == 2);
+			elem = (IndexElem *) linitial(pair);
+			Assert(IsA(elem, IndexElem));
+			opname = (List *) lsecond(pair);
+			Assert(IsA(opname, List));
+
+			index->indexParams = lappend(index->indexParams, elem);
+			index->excludeOpNames = lappend(index->excludeOpNames, opname);
+		}
+
+		return index;
+	}
+
+	/*
+	 * For UNIQUE and PRIMARY KEY, we just have a list of column names.
+	 *
 	 * Make sure referenced keys exist.  If we are making a PRIMARY KEY index,
 	 * also make sure they are NOT NULL, if possible. (Although we could leave
 	 * it to DefineIndex to mark the columns NOT NULL, it's more efficient to
 	 * get it right the first time.)
 	 */
-	foreach(keys, constraint->keys)
+	foreach(lc, constraint->keys)
 	{
-		char	   *key = strVal(lfirst(keys));
+		char	   *key = strVal(lfirst(lc));
 		bool		found = false;
 		ColumnDef  *column = NULL;
 		ListCell   *columns;
+		IndexElem  *iparam;
 
 		foreach(columns, cxt->columns)
 		{
@@ -2000,6 +2093,7 @@ transformConstraintAttrs(ParseState *pstate, List *constraintList)
 	((node) != NULL &&						\
 	 ((node)->contype == CONSTR_PRIMARY ||	\
 	  (node)->contype == CONSTR_UNIQUE ||	\
+	  (node)->contype == CONSTR_EXCLUSION || \
 	  (node)->contype == CONSTR_FOREIGN))
 
 	foreach(clist, constraintList)
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index f4263d39e658e88aab66eb235b6c8b6b6a7585a2..1a7640b3500dc6987e729edd41bd8a7698b88b1d 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.320 2009/12/01 02:31:12 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.321 2009/12/07 05:22:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -797,6 +797,7 @@ ProcessUtility(Node *parsetree,
 							stmt->indexParams,	/* parameters */
 							(Expr *) stmt->whereClause,
 							stmt->options,
+							stmt->excludeOpNames,
 							stmt->unique,
 							stmt->primary,
 							stmt->isconstraint,
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index fc5885e68e2224941ff1ded36433fc187ba759a7..71e614d7004d9e2b5417f6a29287a222a13463a7 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.315 2009/11/20 20:38:11 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.316 2009/12/07 05:22:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -144,6 +144,7 @@ static void decompile_column_index_array(Datum column_index_array, Oid relId,
 							 StringInfo buf);
 static char *pg_get_ruledef_worker(Oid ruleoid, int prettyFlags);
 static char *pg_get_indexdef_worker(Oid indexrelid, int colno,
+					   const Oid *excludeOps,
 					   bool attrsOnly, bool showTblSpc,
 					   int prettyFlags);
 static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
@@ -705,6 +706,7 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
 	Oid			indexrelid = PG_GETARG_OID(0);
 
 	PG_RETURN_TEXT_P(string_to_text(pg_get_indexdef_worker(indexrelid, 0,
+														   NULL,
 														   false, false, 0)));
 }
 
@@ -718,6 +720,7 @@ pg_get_indexdef_ext(PG_FUNCTION_ARGS)
 
 	prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : 0;
 	PG_RETURN_TEXT_P(string_to_text(pg_get_indexdef_worker(indexrelid, colno,
+														   NULL,
 														   colno != 0,
 														   false,
 														   prettyFlags)));
@@ -727,7 +730,7 @@ pg_get_indexdef_ext(PG_FUNCTION_ARGS)
 char *
 pg_get_indexdef_string(Oid indexrelid)
 {
-	return pg_get_indexdef_worker(indexrelid, 0, false, true, 0);
+	return pg_get_indexdef_worker(indexrelid, 0, NULL, false, true, 0);
 }
 
 /* Internal version that just reports the column definitions */
@@ -737,14 +740,23 @@ pg_get_indexdef_columns(Oid indexrelid, bool pretty)
 	int			prettyFlags;
 
 	prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : 0;
-	return pg_get_indexdef_worker(indexrelid, 0, true, false, prettyFlags);
+	return pg_get_indexdef_worker(indexrelid, 0, NULL, true, false, prettyFlags);
 }
 
+/*
+ * Internal workhorse to decompile an index definition.
+ *
+ * This is now used for exclusion constraints as well: if excludeOps is not
+ * NULL then it points to an array of exclusion operator OIDs.
+ */
 static char *
 pg_get_indexdef_worker(Oid indexrelid, int colno,
+					   const Oid *excludeOps,
 					   bool attrsOnly, bool showTblSpc,
 					   int prettyFlags)
 {
+	/* might want a separate isConstraint parameter later */
+	bool		isConstraint = (excludeOps != NULL);
 	HeapTuple	ht_idx;
 	HeapTuple	ht_idxrel;
 	HeapTuple	ht_am;
@@ -842,11 +854,17 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
 	initStringInfo(&buf);
 
 	if (!attrsOnly)
-		appendStringInfo(&buf, "CREATE %sINDEX %s ON %s USING %s (",
-						 idxrec->indisunique ? "UNIQUE " : "",
-						 quote_identifier(NameStr(idxrelrec->relname)),
-						 generate_relation_name(indrelid, NIL),
-						 quote_identifier(NameStr(amrec->amname)));
+	{
+		if (!isConstraint)
+			appendStringInfo(&buf, "CREATE %sINDEX %s ON %s USING %s (",
+							 idxrec->indisunique ? "UNIQUE " : "",
+							 quote_identifier(NameStr(idxrelrec->relname)),
+							 generate_relation_name(indrelid, NIL),
+							 quote_identifier(NameStr(amrec->amname)));
+		else					/* currently, must be EXCLUDE constraint */
+			appendStringInfo(&buf, "EXCLUDE USING %s (",
+							 quote_identifier(NameStr(amrec->amname)));
+	}
 
 	/*
 	 * Report the indexed attributes
@@ -917,6 +935,13 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
 						appendStringInfo(&buf, " NULLS FIRST");
 				}
 			}
+
+			/* Add the exclusion operator if relevant */
+			if (excludeOps != NULL)
+				appendStringInfo(&buf, " WITH %s",
+								 generate_operator_name(excludeOps[keyno],
+														keycoltype,
+														keycoltype));
 		}
 	}
 
@@ -943,8 +968,12 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
 
 			tblspc = get_rel_tablespace(indexrelid);
 			if (OidIsValid(tblspc))
+			{
+				if (isConstraint)
+					appendStringInfoString(&buf, " USING INDEX");
 				appendStringInfo(&buf, " TABLESPACE %s",
 							  quote_identifier(get_tablespace_name(tblspc)));
+			}
 		}
 
 		/*
@@ -968,7 +997,10 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
 			/* Deparse */
 			str = deparse_expression_pretty(node, context, false, false,
 											prettyFlags, 0);
-			appendStringInfo(&buf, " WHERE %s", str);
+			if (isConstraint)
+				appendStringInfo(&buf, " WHERE (%s)", str);
+			else
+				appendStringInfo(&buf, " WHERE %s", str);
 		}
 	}
 
@@ -1244,6 +1276,43 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
 
 				break;
 			}
+		case CONSTRAINT_EXCLUSION:
+			{
+				Oid		 indexOid = conForm->conindid;
+				Datum	 val;
+				bool	 isnull;
+				Datum	*elems;
+				int		 nElems;
+				int		 i;
+				Oid		*operators;
+
+				/* Extract operator OIDs from the pg_constraint tuple */
+				val = SysCacheGetAttr(CONSTROID, tup,
+									  Anum_pg_constraint_conexclop,
+									  &isnull);
+				if (isnull)
+					elog(ERROR, "null conexclop for constraint %u",
+						 constraintId);
+
+				deconstruct_array(DatumGetArrayTypeP(val),
+								  OIDOID, sizeof(Oid), true, 'i',
+								  &elems, NULL, &nElems);
+
+				operators = (Oid *) palloc(nElems * sizeof(Oid));
+				for (i = 0; i < nElems; i++)
+					operators[i] = DatumGetObjectId(elems[i]);
+
+				/* pg_get_indexdef_worker does the rest */
+				/* suppress tablespace because pg_dump wants it that way */
+				appendStringInfoString(&buf,
+									   pg_get_indexdef_worker(indexOid,
+															  0,
+															  operators,
+															  false,
+															  false,
+															  prettyFlags));
+				break;
+			}
 		default:
 			elog(ERROR, "invalid constraint type \"%c\"", conForm->contype);
 			break;
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 3a66e2aa8e7ef49fe06554a1d5eacdc704cd831d..82c4bbc01a9aa35ba70fb5b37e9eab2a63db711c 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.292 2009/09/26 23:08:22 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.293 2009/12/07 05:22:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -60,9 +60,11 @@
 #include "storage/fd.h"
 #include "storage/lmgr.h"
 #include "storage/smgr.h"
+#include "utils/array.h"
 #include "utils/builtins.h"
 #include "utils/fmgroids.h"
 #include "utils/inval.h"
+#include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/relcache.h"
 #include "utils/resowner.h"
@@ -1079,10 +1081,13 @@ RelationInitIndexAccessInfo(Relation relation)
 	memcpy(relation->rd_indoption, indoption->values, natts * sizeof(int16));
 
 	/*
-	 * expressions and predicate cache will be filled later
+	 * expressions, predicate, exclusion caches will be filled later
 	 */
 	relation->rd_indexprs = NIL;
 	relation->rd_indpred = NIL;
+	relation->rd_exclops = NULL;
+	relation->rd_exclprocs = NULL;
+	relation->rd_exclstrats = NULL;
 	relation->rd_amcache = NULL;
 }
 
@@ -3453,6 +3458,130 @@ RelationGetIndexAttrBitmap(Relation relation)
 	return indexattrs;
 }
 
+/*
+ * RelationGetExclusionInfo -- get info about index's exclusion constraint
+ *
+ * This should be called only for an index that is known to have an
+ * associated exclusion constraint.  It returns arrays (palloc'd in caller's
+ * context) of the exclusion operator OIDs, their underlying functions'
+ * OIDs, and their strategy numbers in the index's opclasses.  We cache
+ * all this information since it requires a fair amount of work to get.
+ */
+void
+RelationGetExclusionInfo(Relation indexRelation,
+						 Oid **operators,
+						 Oid **procs,
+						 uint16 **strategies)
+{
+	int			ncols = indexRelation->rd_rel->relnatts;
+	Oid		   *ops;
+	Oid		   *funcs;
+	uint16	   *strats;
+	Relation	conrel;
+	SysScanDesc	conscan;
+	ScanKeyData	skey[1];
+	HeapTuple	htup;
+	bool		found;
+	MemoryContext oldcxt;
+	int			i;
+
+	/* Allocate result space in caller context */
+	*operators = ops = (Oid *) palloc(sizeof(Oid) * ncols);
+	*procs = funcs = (Oid *) palloc(sizeof(Oid) * ncols);
+	*strategies = strats = (uint16 *) palloc(sizeof(uint16) * ncols);
+
+	/* Quick exit if we have the data cached already */
+	if (indexRelation->rd_exclstrats != NULL)
+	{
+		memcpy(ops, indexRelation->rd_exclops, sizeof(Oid) * ncols);
+		memcpy(funcs, indexRelation->rd_exclprocs, sizeof(Oid) * ncols);
+		memcpy(strats, indexRelation->rd_exclstrats, sizeof(uint16) * ncols);
+		return;
+	}
+
+	/*
+	 * Search pg_constraint for the constraint associated with the index.
+	 * To make this not too painfully slow, we use the index on conrelid;
+	 * that will hold the parent relation's OID not the index's own OID.
+	 */
+	ScanKeyInit(&skey[0],
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(indexRelation->rd_index->indrelid));
+
+	conrel = heap_open(ConstraintRelationId, AccessShareLock);
+	conscan = systable_beginscan(conrel, ConstraintRelidIndexId, true,
+								 SnapshotNow, 1, skey);
+	found = false;
+
+	while (HeapTupleIsValid(htup = systable_getnext(conscan)))
+	{
+		Form_pg_constraint	 conform = (Form_pg_constraint) GETSTRUCT(htup);
+		Datum		val;
+		bool		isnull;
+		ArrayType  *arr;
+		int			nelem;
+
+		/* We want the exclusion constraint owning the index */
+		if (conform->contype != CONSTRAINT_EXCLUSION ||
+			conform->conindid != RelationGetRelid(indexRelation))
+			continue;
+
+		/* There should be only one */
+		if (found)
+			elog(ERROR, "unexpected exclusion constraint record found for rel %s",
+				 RelationGetRelationName(indexRelation));
+		found = true;
+
+		/* Extract the operator OIDS from conexclop */
+		val = fastgetattr(htup,
+						  Anum_pg_constraint_conexclop,
+						  conrel->rd_att, &isnull);
+		if (isnull)
+			elog(ERROR, "null conexclop for rel %s",
+				 RelationGetRelationName(indexRelation));
+
+		arr = DatumGetArrayTypeP(val);	/* ensure not toasted */
+		nelem = ARR_DIMS(arr)[0];
+		if (ARR_NDIM(arr) != 1 ||
+			nelem != ncols ||
+			ARR_HASNULL(arr) ||
+			ARR_ELEMTYPE(arr) != OIDOID)
+			elog(ERROR, "conexclop is not a 1-D Oid array");
+
+		memcpy(ops, ARR_DATA_PTR(arr), sizeof(Oid) * ncols);
+	}
+
+	systable_endscan(conscan);
+	heap_close(conrel, AccessShareLock);
+
+	if (!found)
+		elog(ERROR, "exclusion constraint record missing for rel %s",
+			 RelationGetRelationName(indexRelation));
+
+	/* We need the func OIDs and strategy numbers too */
+	for (i = 0; i < ncols; i++)
+	{
+		funcs[i] = get_opcode(ops[i]);
+		strats[i] = get_op_opfamily_strategy(ops[i],
+											 indexRelation->rd_opfamily[i]);
+		/* shouldn't fail, since it was checked at index creation */
+		if (strats[i] == InvalidStrategy)
+			elog(ERROR, "could not find strategy for operator %u in family %u",
+				 ops[i], indexRelation->rd_opfamily[i]);
+	}
+
+	/* Save a copy of the results in the relcache entry. */
+	oldcxt = MemoryContextSwitchTo(indexRelation->rd_indexcxt);
+	indexRelation->rd_exclops = (Oid *) palloc(sizeof(Oid) * ncols);
+	indexRelation->rd_exclprocs = (Oid *) palloc(sizeof(Oid) * ncols);
+	indexRelation->rd_exclstrats = (uint16 *) palloc(sizeof(uint16) * ncols);
+	memcpy(indexRelation->rd_exclops, ops, sizeof(Oid) * ncols);
+	memcpy(indexRelation->rd_exclprocs, funcs, sizeof(Oid) * ncols);
+	memcpy(indexRelation->rd_exclstrats, strats, sizeof(uint16) * ncols);
+	MemoryContextSwitchTo(oldcxt);
+}
+
 
 /*
  *	load_relcache_init_file, write_relcache_init_file
@@ -3768,13 +3897,16 @@ load_relcache_init_file(bool shared)
 		 * format is complex and subject to change).  They must be rebuilt if
 		 * needed by RelationCacheInitializePhase3.  This is not expected to
 		 * be a big performance hit since few system catalogs have such. Ditto
-		 * for index expressions and predicates.
+		 * for index expressions, predicates, and exclusion info.
 		 */
 		rel->rd_rules = NULL;
 		rel->rd_rulescxt = NULL;
 		rel->trigdesc = NULL;
 		rel->rd_indexprs = NIL;
 		rel->rd_indpred = NIL;
+		rel->rd_exclops = NULL;
+		rel->rd_exclprocs = NULL;
+		rel->rd_exclstrats = NULL;
 
 		/*
 		 * Reset transient-state fields in the relcache entry
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 5b3b36757da3528dfca6b5ef14f5653017df7486..a753976139c81aa5625cbada7fc5a38b683d33f9 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -12,7 +12,7 @@
  *	by PostgreSQL
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.553 2009/11/20 20:38:11 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.554 2009/12/07 05:22:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -3680,6 +3680,7 @@ getIndexes(TableInfo tblinfo[], int numTables)
 				i_condeferred,
 				i_contableoid,
 				i_conoid,
+				i_condef,
 				i_tablespace,
 				i_options;
 	int			ntups;
@@ -3710,7 +3711,7 @@ getIndexes(TableInfo tblinfo[], int numTables)
 		 * assume an index won't have more than one internal dependency.
 		 */
 		resetPQExpBuffer(query);
-		if (g_fout->remoteVersion >= 80200)
+		if (g_fout->remoteVersion >= 80500)
 		{
 			appendPQExpBuffer(query,
 							  "SELECT t.tableoid, t.oid, "
@@ -3722,6 +3723,35 @@ getIndexes(TableInfo tblinfo[], int numTables)
 							  "c.condeferrable, c.condeferred, "
 							  "c.tableoid AS contableoid, "
 							  "c.oid AS conoid, "
+							  "pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, "
+							  "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
+							"array_to_string(t.reloptions, ', ') AS options "
+							  "FROM pg_catalog.pg_index i "
+					  "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
+							  "LEFT JOIN pg_catalog.pg_depend d "
+							  "ON (d.classid = t.tableoid "
+							  "AND d.objid = t.oid "
+							  "AND d.deptype = 'i') "
+							  "LEFT JOIN pg_catalog.pg_constraint c "
+							  "ON (d.refclassid = c.tableoid "
+							  "AND d.refobjid = c.oid) "
+							  "WHERE i.indrelid = '%u'::pg_catalog.oid "
+							  "ORDER BY indexname",
+							  tbinfo->dobj.catId.oid);
+		}
+		else if (g_fout->remoteVersion >= 80200)
+		{
+			appendPQExpBuffer(query,
+							  "SELECT t.tableoid, t.oid, "
+							  "t.relname AS indexname, "
+					 "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, "
+							  "t.relnatts AS indnkeys, "
+							  "i.indkey, i.indisclustered, "
+							  "c.contype, c.conname, "
+							  "c.condeferrable, c.condeferred, "
+							  "c.tableoid AS contableoid, "
+							  "c.oid AS conoid, "
+							  "null AS condef, "
 							  "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
 							"array_to_string(t.reloptions, ', ') AS options "
 							  "FROM pg_catalog.pg_index i "
@@ -3749,6 +3779,7 @@ getIndexes(TableInfo tblinfo[], int numTables)
 							  "c.condeferrable, c.condeferred, "
 							  "c.tableoid AS contableoid, "
 							  "c.oid AS conoid, "
+							  "null AS condef, "
 							  "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
 							  "null AS options "
 							  "FROM pg_catalog.pg_index i "
@@ -3776,6 +3807,7 @@ getIndexes(TableInfo tblinfo[], int numTables)
 							  "c.condeferrable, c.condeferred, "
 							  "c.tableoid AS contableoid, "
 							  "c.oid AS conoid, "
+							  "null AS condef, "
 							  "NULL AS tablespace, "
 							  "null AS options "
 							  "FROM pg_catalog.pg_index i "
@@ -3806,6 +3838,7 @@ getIndexes(TableInfo tblinfo[], int numTables)
 							  "false AS condeferred, "
 							  "0::oid AS contableoid, "
 							  "t.oid AS conoid, "
+							  "null AS condef, "
 							  "NULL AS tablespace, "
 							  "null AS options "
 							  "FROM pg_index i, pg_class t "
@@ -3831,6 +3864,7 @@ getIndexes(TableInfo tblinfo[], int numTables)
 							  "false AS condeferred, "
 							  "0::oid AS contableoid, "
 							  "t.oid AS conoid, "
+							  "null AS condef, "
 							  "NULL AS tablespace, "
 							  "null AS options "
 							  "FROM pg_index i, pg_class t "
@@ -3858,6 +3892,7 @@ getIndexes(TableInfo tblinfo[], int numTables)
 		i_condeferred = PQfnumber(res, "condeferred");
 		i_contableoid = PQfnumber(res, "contableoid");
 		i_conoid = PQfnumber(res, "conoid");
+		i_condef = PQfnumber(res, "condef");
 		i_tablespace = PQfnumber(res, "tablespace");
 		i_options = PQfnumber(res, "options");
 
@@ -3895,7 +3930,7 @@ getIndexes(TableInfo tblinfo[], int numTables)
 			indxinfo[j].indisclustered = (PQgetvalue(res, j, i_indisclustered)[0] == 't');
 			contype = *(PQgetvalue(res, j, i_contype));
 
-			if (contype == 'p' || contype == 'u')
+			if (contype == 'p' || contype == 'u' || contype == 'x')
 			{
 				/*
 				 * If we found a constraint matching the index, create an
@@ -3913,7 +3948,10 @@ getIndexes(TableInfo tblinfo[], int numTables)
 				constrinfo[j].contable = tbinfo;
 				constrinfo[j].condomain = NULL;
 				constrinfo[j].contype = contype;
-				constrinfo[j].condef = NULL;
+				if (contype == 'x')
+					constrinfo[j].condef = strdup(PQgetvalue(res, j, i_condef));
+				else
+					constrinfo[j].condef = NULL;
 				constrinfo[j].confrelid = InvalidOid;
 				constrinfo[j].conindex = indxinfo[j].dobj.dumpId;
 				constrinfo[j].condeferrable = *(PQgetvalue(res, j, i_condeferrable)) == 't';
@@ -10728,7 +10766,9 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo)
 	q = createPQExpBuffer();
 	delq = createPQExpBuffer();
 
-	if (coninfo->contype == 'p' || coninfo->contype == 'u')
+	if (coninfo->contype == 'p' ||
+		coninfo->contype == 'u' ||
+		coninfo->contype == 'x')
 	{
 		/* Index-related constraint */
 		IndxInfo   *indxinfo;
@@ -10745,37 +10785,46 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo)
 
 		appendPQExpBuffer(q, "ALTER TABLE ONLY %s\n",
 						  fmtId(tbinfo->dobj.name));
-		appendPQExpBuffer(q, "    ADD CONSTRAINT %s %s (",
-						  fmtId(coninfo->dobj.name),
-						  coninfo->contype == 'p' ? "PRIMARY KEY" : "UNIQUE");
+		appendPQExpBuffer(q, "    ADD CONSTRAINT %s ",
+						  fmtId(coninfo->dobj.name));
 
-		for (k = 0; k < indxinfo->indnkeys; k++)
+		if (coninfo->condef)
+		{
+			/* pg_get_constraintdef should have provided everything */
+			appendPQExpBuffer(q, "%s;\n", coninfo->condef);
+		}
+		else
 		{
-			int			indkey = (int) indxinfo->indkeys[k];
-			const char *attname;
+			appendPQExpBuffer(q, "%s (",
+							  coninfo->contype == 'p' ? "PRIMARY KEY" : "UNIQUE");
+			for (k = 0; k < indxinfo->indnkeys; k++)
+			{
+				int			indkey = (int) indxinfo->indkeys[k];
+				const char *attname;
 
-			if (indkey == InvalidAttrNumber)
-				break;
-			attname = getAttrName(indkey, tbinfo);
+				if (indkey == InvalidAttrNumber)
+					break;
+				attname = getAttrName(indkey, tbinfo);
 
-			appendPQExpBuffer(q, "%s%s",
-							  (k == 0) ? "" : ", ",
-							  fmtId(attname));
-		}
+				appendPQExpBuffer(q, "%s%s",
+								  (k == 0) ? "" : ", ",
+								  fmtId(attname));
+			}
 
-		appendPQExpBuffer(q, ")");
+			appendPQExpBuffer(q, ")");
 
-		if (indxinfo->options && strlen(indxinfo->options) > 0)
-			appendPQExpBuffer(q, " WITH (%s)", indxinfo->options);
+			if (indxinfo->options && strlen(indxinfo->options) > 0)
+				appendPQExpBuffer(q, " WITH (%s)", indxinfo->options);
 
-		if (coninfo->condeferrable)
-		{
-			appendPQExpBuffer(q, " DEFERRABLE");
-			if (coninfo->condeferred)
-				appendPQExpBuffer(q, " INITIALLY DEFERRED");
-		}
+			if (coninfo->condeferrable)
+			{
+				appendPQExpBuffer(q, " DEFERRABLE");
+				if (coninfo->condeferred)
+					appendPQExpBuffer(q, " INITIALLY DEFERRED");
+			}
 
-		appendPQExpBuffer(q, ";\n");
+			appendPQExpBuffer(q, ";\n");
+		}
 
 		/* If the index is clustered, we need to record that. */
 		if (indxinfo->indisclustered)
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 8fd456fe34e096b83b0c9494a41026b796f4fae6..8ca21dfc0d5c1280808ea137fff9ccf7b668b8fc 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -8,7 +8,7 @@
  *
  * Copyright (c) 2000-2009, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.231 2009/11/11 21:07:41 petere Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.232 2009/12/07 05:22:23 tgl Exp $
  */
 #include "postgres_fe.h"
 
@@ -1105,6 +1105,7 @@ describeOneTableDetails(const char *schemaname,
 		bool		hasrules;
 		bool		hastriggers;
 		bool		hasoids;
+		bool		hasexclusion;
 		Oid			tablespace;
 		char	   *reloptions;
 	}			tableinfo;
@@ -1121,7 +1122,22 @@ describeOneTableDetails(const char *schemaname,
 	initPQExpBuffer(&tmpbuf);
 
 	/* Get general table info */
-	if (pset.sversion >= 80400)
+	if (pset.sversion >= 80500)
+	{
+		printfPQExpBuffer(&buf,
+			  "SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
+						  "c.relhastriggers, c.relhasoids, "
+						  "%s, c.reltablespace, c.relhasexclusion\n"
+						  "FROM pg_catalog.pg_class c\n "
+		   "LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)\n"
+						  "WHERE c.oid = '%s'\n",
+						  (verbose ?
+						   "pg_catalog.array_to_string(c.reloptions || "
+						   "array(select 'toast.' || x from pg_catalog.unnest(tc.reloptions) x), ', ')\n"
+						   : "''"),
+						  oid);
+	}
+	else if (pset.sversion >= 80400)
 	{
 		printfPQExpBuffer(&buf,
 			  "SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
@@ -1185,10 +1201,12 @@ describeOneTableDetails(const char *schemaname,
 	tableinfo.hasrules = strcmp(PQgetvalue(res, 0, 3), "t") == 0;
 	tableinfo.hastriggers = strcmp(PQgetvalue(res, 0, 4), "t") == 0;
 	tableinfo.hasoids = strcmp(PQgetvalue(res, 0, 5), "t") == 0;
-	tableinfo.reloptions = pset.sversion >= 80200 ?
+	tableinfo.reloptions = (pset.sversion >= 80200) ?
 		strdup(PQgetvalue(res, 0, 6)) : 0;
 	tableinfo.tablespace = (pset.sversion >= 80000) ?
 		atooid(PQgetvalue(res, 0, 7)) : 0;
+	tableinfo.hasexclusion = (pset.sversion >= 80500) ?
+		strcmp(PQgetvalue(res, 0, 8), "t") == 0 : false;
 	PQclear(res);
 	res = NULL;
 
@@ -1642,6 +1660,38 @@ describeOneTableDetails(const char *schemaname,
 			PQclear(result);
 		}
 
+		/* print exclusion constraints */
+		if (tableinfo.hasexclusion)
+		{
+			printfPQExpBuffer(&buf,
+							  "SELECT r.conname, "
+							  "pg_catalog.pg_get_constraintdef(r.oid, true)\n"
+							  "FROM pg_catalog.pg_constraint r\n"
+							  "WHERE r.conrelid = '%s' AND r.contype = 'x'\n"
+							  "ORDER BY 1",
+							  oid);
+			result = PSQLexec(buf.data, false);
+			if (!result)
+				goto error_return;
+			else
+				tuples = PQntuples(result);
+
+			if (tuples > 0)
+			{
+				printTableAddFooter(&cont, _("Exclusion constraints:"));
+				for (i = 0; i < tuples; i++)
+				{
+					/* untranslated contraint name and def */
+					printfPQExpBuffer(&buf, "    \"%s\" %s",
+									  PQgetvalue(result, i, 0),
+									  PQgetvalue(result, i, 1));
+
+					printTableAddFooter(&cont, buf.data);
+				}
+			}
+			PQclear(result);
+		}
+
 		/* print foreign-key constraints (there are none if no triggers) */
 		if (tableinfo.hastriggers)
 		{
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 83ba592aa3718cafe8def43a433bd7c9bc6e43aa..9b935720972924562d848d431be9c9e955234627 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.555 2009/12/05 21:43:35 petere Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.556 2009/12/07 05:22:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	200912051
+#define CATALOG_VERSION_NO	200912071
 
 #endif
diff --git a/src/include/catalog/pg_attribute.h b/src/include/catalog/pg_attribute.h
index f5a737f9f0080b9c5a9b7f2e6daf2e12c23a75e7..8eae51f0986968955963370a73151ece0228d82e 100644
--- a/src/include/catalog/pg_attribute.h
+++ b/src/include/catalog/pg_attribute.h
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_attribute.h,v 1.154 2009/10/07 22:14:25 alvherre Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_attribute.h,v 1.155 2009/12/07 05:22:23 tgl Exp $
  *
  * NOTES
  *	  the genbki.sh script reads this file and generates .bki
@@ -426,12 +426,13 @@ DATA(insert ( 1249 tableoid			26 0 0  4  -7 0 -1 -1 t p i t f f t 0 _null_));
 { 1259, {"relchecks"},	   21, -1, 0,	2, 17, 0, -1, -1, true, 'p', 's', true, false, false, true, 0, { 0 } }, \
 { 1259, {"relhasoids"},    16, -1, 0,	1, 18, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
 { 1259, {"relhaspkey"},    16, -1, 0,	1, 19, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
-{ 1259, {"relhasrules"},   16, -1, 0,	1, 20, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
-{ 1259, {"relhastriggers"},16, -1, 0,	1, 21, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
-{ 1259, {"relhassubclass"},16, -1, 0,	1, 22, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
-{ 1259, {"relfrozenxid"},  28, -1, 0,	4, 23, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
-{ 1259, {"relacl"},		 1034, -1, 0, -1, 24, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } }, \
-{ 1259, {"reloptions"},  1009, -1, 0, -1, 25, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } }
+{ 1259, {"relhasexclusion"},16, -1, 0,	1, 20, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
+{ 1259, {"relhasrules"},   16, -1, 0,	1, 21, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
+{ 1259, {"relhastriggers"},16, -1, 0,	1, 22, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
+{ 1259, {"relhassubclass"},16, -1, 0,	1, 23, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
+{ 1259, {"relfrozenxid"},  28, -1, 0,	4, 24, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
+{ 1259, {"relacl"},		 1034, -1, 0, -1, 25, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } }, \
+{ 1259, {"reloptions"},  1009, -1, 0, -1, 26, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } }
 
 DATA(insert ( 1259 relname			19 -1 0 NAMEDATALEN	1 0 -1 -1 f p c t f f t 0 _null_));
 DATA(insert ( 1259 relnamespace		26 -1 0 4   2 0 -1 -1 t p i t f f t 0 _null_));
@@ -452,12 +453,13 @@ DATA(insert ( 1259 relnatts			21 -1 0 2  16 0 -1 -1 t p s t f f t 0 _null_));
 DATA(insert ( 1259 relchecks		21 -1 0 2  17 0 -1 -1 t p s t f f t 0 _null_));
 DATA(insert ( 1259 relhasoids		16 -1 0 1  18 0 -1 -1 t p c t f f t 0 _null_));
 DATA(insert ( 1259 relhaspkey		16 -1 0 1  19 0 -1 -1 t p c t f f t 0 _null_));
-DATA(insert ( 1259 relhasrules		16 -1 0 1  20 0 -1 -1 t p c t f f t 0 _null_));
-DATA(insert ( 1259 relhastriggers	16 -1 0 1  21 0 -1 -1 t p c t f f t 0 _null_));
-DATA(insert ( 1259 relhassubclass	16 -1 0 1  22 0 -1 -1 t p c t f f t 0 _null_));
-DATA(insert ( 1259 relfrozenxid		28 -1 0 4  23 0 -1 -1 t p i t f f t 0 _null_));
-DATA(insert ( 1259 relacl		  1034 -1 0 -1 24 1 -1 -1 f x i f f f t 0 _null_));
-DATA(insert ( 1259 reloptions	  1009 -1 0 -1 25 1 -1 -1 f x i f f f t 0 _null_));
+DATA(insert ( 1259 relhasexclusion	16 -1 0 1  20 0 -1 -1 t p c t f f t 0 _null_));
+DATA(insert ( 1259 relhasrules		16 -1 0 1  21 0 -1 -1 t p c t f f t 0 _null_));
+DATA(insert ( 1259 relhastriggers	16 -1 0 1  22 0 -1 -1 t p c t f f t 0 _null_));
+DATA(insert ( 1259 relhassubclass	16 -1 0 1  23 0 -1 -1 t p c t f f t 0 _null_));
+DATA(insert ( 1259 relfrozenxid		28 -1 0 4  24 0 -1 -1 t p i t f f t 0 _null_));
+DATA(insert ( 1259 relacl		  1034 -1 0 -1 25 1 -1 -1 f x i f f f t 0 _null_));
+DATA(insert ( 1259 reloptions	  1009 -1 0 -1 26 1 -1 -1 f x i f f f t 0 _null_));
 DATA(insert ( 1259 ctid				27 0 0  6  -1 0 -1 -1 f p s t f f t 0 _null_));
 DATA(insert ( 1259 oid				26 0 0  4  -2 0 -1 -1 t p i t f f t 0 _null_));
 DATA(insert ( 1259 xmin				28 0 0  4  -3 0 -1 -1 t p i t f f t 0 _null_));
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index 2ab4e9fda0236a575bf067a906bb71760c52cd5b..5746e447e4c39b5339c3d2fbd65fb1942098b1ed 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_class.h,v 1.116 2009/09/26 22:42:02 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_class.h,v 1.117 2009/12/07 05:22:23 tgl Exp $
  *
  * NOTES
  *	  the genbki.sh script reads this file and generates .bki
@@ -56,6 +56,7 @@ CATALOG(pg_class,1259) BKI_BOOTSTRAP BKI_ROWTYPE_OID(83)
 	int2		relchecks;		/* # of CHECK constraints for class */
 	bool		relhasoids;		/* T if we generate OIDs for rows of rel */
 	bool		relhaspkey;		/* has (or has had) PRIMARY KEY index */
+	bool		relhasexclusion; /* has (or has had) exclusion constraint */
 	bool		relhasrules;	/* has (or has had) any rules */
 	bool		relhastriggers; /* has (or has had) any TRIGGERs */
 	bool		relhassubclass; /* has (or has had) derived classes */
@@ -87,7 +88,7 @@ typedef FormData_pg_class *Form_pg_class;
  * ----------------
  */
 
-#define Natts_pg_class					25
+#define Natts_pg_class					26
 #define Anum_pg_class_relname			1
 #define Anum_pg_class_relnamespace		2
 #define Anum_pg_class_reltype			3
@@ -107,12 +108,13 @@ typedef FormData_pg_class *Form_pg_class;
 #define Anum_pg_class_relchecks			17
 #define Anum_pg_class_relhasoids		18
 #define Anum_pg_class_relhaspkey		19
-#define Anum_pg_class_relhasrules		20
-#define Anum_pg_class_relhastriggers	21
-#define Anum_pg_class_relhassubclass	22
-#define Anum_pg_class_relfrozenxid		23
-#define Anum_pg_class_relacl			24
-#define Anum_pg_class_reloptions		25
+#define Anum_pg_class_relhasexclusion	20
+#define Anum_pg_class_relhasrules		21
+#define Anum_pg_class_relhastriggers	22
+#define Anum_pg_class_relhassubclass	23
+#define Anum_pg_class_relfrozenxid		24
+#define Anum_pg_class_relacl			25
+#define Anum_pg_class_reloptions		26
 
 /* ----------------
  *		initial contents of pg_class
@@ -124,13 +126,13 @@ typedef FormData_pg_class *Form_pg_class;
  */
 
 /* Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId */
-DATA(insert OID = 1247 (  pg_type		PGNSP 71 PGUID 0 1247 0 0 0 0 0 f f f r 28 0 t f f f f 3 _null_ _null_ ));
+DATA(insert OID = 1247 (  pg_type		PGNSP 71 PGUID 0 1247 0 0 0 0 0 f f f r 28 0 t f f f f f 3 _null_ _null_ ));
 DESCR("");
-DATA(insert OID = 1249 (  pg_attribute	PGNSP 75 PGUID 0 1249 0 0 0 0 0 f f f r 19 0 f f f f f 3 _null_ _null_ ));
+DATA(insert OID = 1249 (  pg_attribute	PGNSP 75 PGUID 0 1249 0 0 0 0 0 f f f r 19 0 f f f f f f 3 _null_ _null_ ));
 DESCR("");
-DATA(insert OID = 1255 (  pg_proc		PGNSP 81 PGUID 0 1255 0 0 0 0 0 f f f r 25 0 t f f f f 3 _null_ _null_ ));
+DATA(insert OID = 1255 (  pg_proc		PGNSP 81 PGUID 0 1255 0 0 0 0 0 f f f r 25 0 t f f f f f 3 _null_ _null_ ));
 DESCR("");
-DATA(insert OID = 1259 (  pg_class		PGNSP 83 PGUID 0 1259 0 0 0 0 0 f f f r 25 0 t f f f f 3 _null_ _null_ ));
+DATA(insert OID = 1259 (  pg_class		PGNSP 83 PGUID 0 1259 0 0 0 0 0 f f f r 26 0 t f f f f f 3 _null_ _null_ ));
 DESCR("");
 
 #define		  RELKIND_INDEX			  'i'		/* secondary index */
diff --git a/src/include/catalog/pg_constraint.h b/src/include/catalog/pg_constraint.h
index 9822fc8c082e130bcdc2eed6453f32e34a2e1c0e..87258a231a3a8fe3d8e788205be239bcc8065d67 100644
--- a/src/include/catalog/pg_constraint.h
+++ b/src/include/catalog/pg_constraint.h
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_constraint.h,v 1.33 2009/10/12 19:49:24 adunstan Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_constraint.h,v 1.34 2009/12/07 05:22:23 tgl Exp $
  *
  * NOTES
  *	  the genbki.sh script reads this file and generates .bki
@@ -119,6 +119,12 @@ CATALOG(pg_constraint,2606)
 	 */
 	Oid			conffeqop[1];
 
+	/*
+	 * If an exclusion constraint, the OIDs of the exclusion operators for
+	 * each column of the constraint
+	 */
+	Oid			conexclop[1];
+
 	/*
 	 * If a check constraint, nodeToString representation of expression
 	 */
@@ -141,7 +147,7 @@ typedef FormData_pg_constraint *Form_pg_constraint;
  *		compiler constants for pg_constraint
  * ----------------
  */
-#define Natts_pg_constraint					21
+#define Natts_pg_constraint					22
 #define Anum_pg_constraint_conname			1
 #define Anum_pg_constraint_connamespace		2
 #define Anum_pg_constraint_contype			3
@@ -161,8 +167,9 @@ typedef FormData_pg_constraint *Form_pg_constraint;
 #define Anum_pg_constraint_conpfeqop		17
 #define Anum_pg_constraint_conppeqop		18
 #define Anum_pg_constraint_conffeqop		19
-#define Anum_pg_constraint_conbin			20
-#define Anum_pg_constraint_consrc			21
+#define Anum_pg_constraint_conexclop		20
+#define Anum_pg_constraint_conbin			21
+#define Anum_pg_constraint_consrc			22
 
 
 /* Valid values for contype */
@@ -170,6 +177,7 @@ typedef FormData_pg_constraint *Form_pg_constraint;
 #define CONSTRAINT_FOREIGN			'f'
 #define CONSTRAINT_PRIMARY			'p'
 #define CONSTRAINT_UNIQUE			'u'
+#define CONSTRAINT_EXCLUSION		'x'
 
 /*
  * Valid values for confupdtype and confdeltype are the FKCONSTR_ACTION_xxx
@@ -209,6 +217,7 @@ extern Oid CreateConstraintEntry(const char *constraintName,
 					  char foreignUpdateType,
 					  char foreignDeleteType,
 					  char foreignMatchType,
+					  const Oid *exclOp,
 					  Node *conExpr,
 					  const char *conBin,
 					  const char *conSrc,
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index 89bb227a90f27caa6d7c861a6ae390be0b6e5c52..1b665ff8550ba257cc4a78077d945f7b9cbb4652 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/commands/defrem.h,v 1.97 2009/09/22 23:43:41 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/commands/defrem.h,v 1.98 2009/12/07 05:22:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -26,6 +26,7 @@ extern void DefineIndex(RangeVar *heapRelation,
 			List *attributeList,
 			Expr *predicate,
 			List *options,
+			List *exclusionOpNames,
 			bool unique,
 			bool primary,
 			bool isconstraint,
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index ba2f42d68621f915d0ed687de1f9c62361847725..76c075f75f949b5ab784ce2a5cee68ebc5325d41 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.163 2009/10/26 02:26:41 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.164 2009/12/07 05:22:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -321,6 +321,12 @@ extern void ExecOpenIndices(ResultRelInfo *resultRelInfo);
 extern void ExecCloseIndices(ResultRelInfo *resultRelInfo);
 extern List *ExecInsertIndexTuples(TupleTableSlot *slot, ItemPointer tupleid,
 					  EState *estate, bool is_vacuum_full);
+extern bool check_exclusion_constraint(Relation heap, Relation index,
+									   IndexInfo *indexInfo,
+									   ItemPointer tupleid,
+									   Datum *values, bool *isnull,
+									   EState *estate,
+									   bool newIndex, bool errorOK);
 
 extern void RegisterExprContextCallback(ExprContext *econtext,
 							ExprContextCallbackFunction function,
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 3bff0b5a6d1f77f8901e52f5ea5fff77671c58ae..7e4cfe9d71fc3a25522a6986a084aa95b123daf4 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.212 2009/11/20 20:38:11 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.213 2009/12/07 05:22:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -40,6 +40,9 @@
  *		ExpressionsState	exec state for expressions, or NIL if none
  *		Predicate			partial-index predicate, or NIL if none
  *		PredicateState		exec state for predicate, or NIL if none
+ *		ExclusionOps		Per-column exclusion operators, or NULL if none
+ *		ExclusionProcs		Underlying function OIDs for ExclusionOps
+ *		ExclusionStrats		Opclass strategy numbers for ExclusionOps
  *		Unique				is it a unique index?
  *		ReadyForInserts		is it valid for inserts?
  *		Concurrent			are we doing a concurrent index build?
@@ -58,6 +61,9 @@ typedef struct IndexInfo
 	List	   *ii_ExpressionsState;	/* list of ExprState */
 	List	   *ii_Predicate;	/* list of Expr */
 	List	   *ii_PredicateState;		/* list of ExprState */
+	Oid		   *ii_ExclusionOps;		/* array with one entry per column */
+	Oid		   *ii_ExclusionProcs;		/* array with one entry per column */
+	uint16	   *ii_ExclusionStrats;		/* array with one entry per column */
 	bool		ii_Unique;
 	bool		ii_ReadyForInserts;
 	bool		ii_Concurrent;
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index ad95ac9bb4d160486f223fb5f3454b66c801e42c..a791223e6fb48e892a57340dde46ac904708859b 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -13,7 +13,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.416 2009/11/20 20:38:11 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.417 2009/12/07 05:22:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1395,6 +1395,7 @@ typedef enum ConstrType			/* types of constraints */
 	CONSTR_CHECK,
 	CONSTR_PRIMARY,
 	CONSTR_UNIQUE,
+	CONSTR_EXCLUSION,
 	CONSTR_FOREIGN,
 	CONSTR_ATTR_DEFERRABLE,		/* attributes for previous constraint node */
 	CONSTR_ATTR_NOT_DEFERRABLE,
@@ -1429,10 +1430,18 @@ typedef struct Constraint
 	Node	   *raw_expr;		/* expr, as untransformed parse tree */
 	char	   *cooked_expr;	/* expr, as nodeToString representation */
 
-	/* Fields used for index constraints (UNIQUE and PRIMARY KEY): */
+	/* Fields used for unique constraints (UNIQUE and PRIMARY KEY): */
 	List	   *keys;			/* String nodes naming referenced column(s) */
+
+	/* Fields used for EXCLUSION constraints: */
+	List	   *exclusions;		/* list of (IndexElem, operator name) pairs */
+
+	/* Fields used for index constraints (UNIQUE, PRIMARY KEY, EXCLUSION): */
 	List	   *options;		/* options from WITH clause */
 	char	   *indexspace;		/* index tablespace; NULL for default */
+	/* These could be, but currently are not, used for UNIQUE/PKEY: */
+	char	   *access_method;	/* index access method; NULL for default */
+	Node	   *where_clause;	/* partial index predicate */
 
 	/* Fields used for FOREIGN KEY constraints: */
 	RangeVar   *pktable;		/* Primary key table */
@@ -1880,6 +1889,7 @@ typedef struct IndexStmt
 	List	   *indexParams;	/* a list of IndexElem */
 	List	   *options;		/* options from WITH clause */
 	Node	   *whereClause;	/* qualification (partial-index predicate) */
+	List	   *excludeOpNames;	/* exclusion operator names, or NIL if none */
 	bool		unique;			/* is index unique? */
 	bool		primary;		/* is index on primary key? */
 	bool		isconstraint;	/* is it from a CONSTRAINT clause? */
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 42087742fa18c8e1c0e4aae583dc8db361b22877..f07057c430644341c7d6cbf2d935cee6b2a5f589 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -11,7 +11,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/include/parser/kwlist.h,v 1.6 2009/11/05 23:24:27 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/include/parser/kwlist.h,v 1.7 2009/12/07 05:22:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -143,6 +143,7 @@ PG_KEYWORD("end", END_P, RESERVED_KEYWORD)
 PG_KEYWORD("enum", ENUM_P, UNRESERVED_KEYWORD)
 PG_KEYWORD("escape", ESCAPE, UNRESERVED_KEYWORD)
 PG_KEYWORD("except", EXCEPT, RESERVED_KEYWORD)
+PG_KEYWORD("exclude", EXCLUDE, UNRESERVED_KEYWORD)
 PG_KEYWORD("excluding", EXCLUDING, UNRESERVED_KEYWORD)
 PG_KEYWORD("exclusive", EXCLUSIVE, UNRESERVED_KEYWORD)
 PG_KEYWORD("execute", EXECUTE, UNRESERVED_KEYWORD)
diff --git a/src/include/utils/errcodes.h b/src/include/utils/errcodes.h
index 6524b8c5fe4eead9dc5a0ad2ec379dbb59c9894b..91f39b79832ff9ef3c82e6c13e8ac4857ffcd7f2 100644
--- a/src/include/utils/errcodes.h
+++ b/src/include/utils/errcodes.h
@@ -11,7 +11,7 @@
  *
  * Copyright (c) 2003-2009, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/include/utils/errcodes.h,v 1.29 2009/03/04 10:55:00 petere Exp $
+ * $PostgreSQL: pgsql/src/include/utils/errcodes.h,v 1.30 2009/12/07 05:22:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -167,6 +167,7 @@
 #define ERRCODE_FOREIGN_KEY_VIOLATION		MAKE_SQLSTATE('2','3', '5','0','3')
 #define ERRCODE_UNIQUE_VIOLATION			MAKE_SQLSTATE('2','3', '5','0','5')
 #define ERRCODE_CHECK_VIOLATION				MAKE_SQLSTATE('2','3', '5','1','4')
+#define ERRCODE_EXCLUSION_VIOLATION			MAKE_SQLSTATE('2','3', 'P','0','1')
 
 /* Class 24 - Invalid Cursor State */
 #define ERRCODE_INVALID_CURSOR_STATE		MAKE_SQLSTATE('2','4', '0','0','0')
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 0ef6642f9805983f9cdee36bc19522d1bc0d2cb1..b41f87f25599d80c9afcd5b1c015a83bd71037f1 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/rel.h,v 1.116 2009/11/20 20:38:11 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/utils/rel.h,v 1.117 2009/12/07 05:22:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -196,6 +196,9 @@ typedef struct RelationData
 	int16	   *rd_indoption;	/* per-column AM-specific flags */
 	List	   *rd_indexprs;	/* index expression trees, if any */
 	List	   *rd_indpred;		/* index predicate tree, if any */
+	Oid		   *rd_exclops;		/* OIDs of exclusion operators, if any */
+	Oid		   *rd_exclprocs;	/* OIDs of exclusion ops' procs, if any */
+	uint16	   *rd_exclstrats;	/* exclusion ops' strategy numbers, if any */
 	void	   *rd_amcache;		/* available for use by index AM */
 
 	/*
diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h
index 007532fb0e5b56376712453a547122fe2721d8d1..4207e7ee4b0fd1a5eb2640f66454db79af317f1b 100644
--- a/src/include/utils/relcache.h
+++ b/src/include/utils/relcache.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/relcache.h,v 1.64 2009/08/12 20:53:31 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/utils/relcache.h,v 1.65 2009/12/07 05:22:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -43,6 +43,10 @@ extern Oid	RelationGetOidIndex(Relation relation);
 extern List *RelationGetIndexExpressions(Relation relation);
 extern List *RelationGetIndexPredicate(Relation relation);
 extern Bitmapset *RelationGetIndexAttrBitmap(Relation relation);
+extern void RelationGetExclusionInfo(Relation indexRelation,
+									 Oid **operators,
+									 Oid **procs,
+									 uint16 **strategies);
 
 extern void RelationSetIndexList(Relation relation,
 					 List *indexIds, Oid oidIndex);
diff --git a/src/pl/plpgsql/src/plerrcodes.h b/src/pl/plpgsql/src/plerrcodes.h
index a2de61d03efdf27cf52a706fab2281cb6a0e9edd..2fdab2ef3b4d80c3743a71c8c166090d99e0bde2 100644
--- a/src/pl/plpgsql/src/plerrcodes.h
+++ b/src/pl/plpgsql/src/plerrcodes.h
@@ -9,7 +9,7 @@
  *
  * Copyright (c) 2003-2009, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/pl/plpgsql/src/plerrcodes.h,v 1.18 2009/03/04 10:55:00 petere Exp $
+ * $PostgreSQL: pgsql/src/pl/plpgsql/src/plerrcodes.h,v 1.19 2009/12/07 05:22:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -303,6 +303,10 @@
 	"check_violation", ERRCODE_CHECK_VIOLATION
 },
 
+{
+	"exclusion_violation", ERRCODE_EXCLUSION_VIOLATION
+},
+
 {
 	"invalid_cursor_state", ERRCODE_INVALID_CURSOR_STATE
 },
diff --git a/src/test/regress/input/constraints.source b/src/test/regress/input/constraints.source
index 178b159c70163eceee3faa1af613d76e09823526..ee396f370372a3198a3b1fb490352b5bc84ca0ea 100644
--- a/src/test/regress/input/constraints.source
+++ b/src/test/regress/input/constraints.source
@@ -5,6 +5,7 @@
 --  - CHECK clauses
 --  - PRIMARY KEY clauses
 --  - UNIQUE clauses
+--  - EXCLUDE clauses
 --
 
 --
@@ -366,3 +367,61 @@ COMMIT;
 SELECT * FROM unique_tbl;
 
 DROP TABLE unique_tbl;
+
+--
+-- EXCLUDE constraints
+--
+
+CREATE TABLE circles (
+  c1 CIRCLE,
+  c2 TEXT,
+  EXCLUDE USING gist
+    (c1 WITH &&, (c2::circle) WITH ~=)
+    WHERE (circle_center(c1) <> '(0,0)')
+);
+
+-- these should succeed because they don't match the index predicate
+INSERT INTO circles VALUES('<(0,0), 5>', '<(0,0), 5>');
+INSERT INTO circles VALUES('<(0,0), 5>', '<(0,0), 5>');
+
+-- succeed
+INSERT INTO circles VALUES('<(10,10), 10>', '<(0,0), 5>');
+-- fail, overlaps
+INSERT INTO circles VALUES('<(20,20), 10>', '<(0,0), 5>');
+-- succeed because c1 doesn't overlap
+INSERT INTO circles VALUES('<(20,20), 1>', '<(0,0), 5>');
+-- succeed because c2 is not the same
+INSERT INTO circles VALUES('<(20,20), 10>', '<(1,1), 5>');
+
+-- should fail on existing data without the WHERE clause
+ALTER TABLE circles ADD EXCLUDE USING gist
+  (c1 WITH &&, (c2::circle) WITH ~=);
+
+DROP TABLE circles;
+
+-- Check deferred exclusion constraint
+
+CREATE TABLE deferred_excl (
+  f1 int,
+  CONSTRAINT deferred_excl_con EXCLUDE (f1 WITH =) INITIALLY DEFERRED
+);
+
+INSERT INTO deferred_excl VALUES(1);
+INSERT INTO deferred_excl VALUES(2);
+INSERT INTO deferred_excl VALUES(1); -- fail
+BEGIN;
+INSERT INTO deferred_excl VALUES(2); -- no fail here
+COMMIT; -- should fail here
+BEGIN;
+INSERT INTO deferred_excl VALUES(3);
+INSERT INTO deferred_excl VALUES(3); -- no fail here
+COMMIT; -- should fail here
+
+ALTER TABLE deferred_excl DROP CONSTRAINT deferred_excl_con;
+
+-- This should fail, but worth testing because of HOT updates
+UPDATE deferred_excl SET f1 = 3;
+
+ALTER TABLE deferred_excl ADD EXCLUDE (f1 WITH =);
+
+DROP TABLE deferred_excl;
diff --git a/src/test/regress/output/constraints.source b/src/test/regress/output/constraints.source
index 56dbf7a0555e9e59aff5a875264dba4e241ef595..8928ca8beba7b0683bdf53830e12b819729ce1df 100644
--- a/src/test/regress/output/constraints.source
+++ b/src/test/regress/output/constraints.source
@@ -5,6 +5,7 @@
 --  - CHECK clauses
 --  - PRIMARY KEY clauses
 --  - UNIQUE clauses
+--  - EXCLUDE clauses
 --
 --
 -- DEFAULT syntax
@@ -512,3 +513,64 @@ SELECT * FROM unique_tbl;
 (5 rows)
 
 DROP TABLE unique_tbl;
+--
+-- EXCLUDE constraints
+--
+CREATE TABLE circles (
+  c1 CIRCLE,
+  c2 TEXT,
+  EXCLUDE USING gist
+    (c1 WITH &&, (c2::circle) WITH ~=)
+    WHERE (circle_center(c1) <> '(0,0)')
+);
+NOTICE:  CREATE TABLE / EXCLUDE will create implicit index "circles_c1_exclusion" for table "circles"
+-- these should succeed because they don't match the index predicate
+INSERT INTO circles VALUES('<(0,0), 5>', '<(0,0), 5>');
+INSERT INTO circles VALUES('<(0,0), 5>', '<(0,0), 5>');
+-- succeed
+INSERT INTO circles VALUES('<(10,10), 10>', '<(0,0), 5>');
+-- fail, overlaps
+INSERT INTO circles VALUES('<(20,20), 10>', '<(0,0), 5>');
+ERROR:  conflicting key value violates exclusion constraint "circles_c1_exclusion"
+DETAIL:  Key (c1, (c2::circle))=(<(20,20),10>, <(0,0),5>) conflicts with existing key (c1, (c2::circle))=(<(10,10),10>, <(0,0),5>).
+-- succeed because c1 doesn't overlap
+INSERT INTO circles VALUES('<(20,20), 1>', '<(0,0), 5>');
+-- succeed because c2 is not the same
+INSERT INTO circles VALUES('<(20,20), 10>', '<(1,1), 5>');
+-- should fail on existing data without the WHERE clause
+ALTER TABLE circles ADD EXCLUDE USING gist
+  (c1 WITH &&, (c2::circle) WITH ~=);
+NOTICE:  ALTER TABLE / ADD EXCLUDE will create implicit index "circles_c1_exclusion1" for table "circles"
+ERROR:  could not create exclusion constraint "circles_c1_exclusion1"
+DETAIL:  Key (c1, (c2::circle))=(<(0,0),5>, <(0,0),5>) conflicts with key (c1, (c2::circle))=(<(0,0),5>, <(0,0),5>).
+DROP TABLE circles;
+-- Check deferred exclusion constraint
+CREATE TABLE deferred_excl (
+  f1 int,
+  CONSTRAINT deferred_excl_con EXCLUDE (f1 WITH =) INITIALLY DEFERRED
+);
+NOTICE:  CREATE TABLE / EXCLUDE will create implicit index "deferred_excl_con" for table "deferred_excl"
+INSERT INTO deferred_excl VALUES(1);
+INSERT INTO deferred_excl VALUES(2);
+INSERT INTO deferred_excl VALUES(1); -- fail
+ERROR:  conflicting key value violates exclusion constraint "deferred_excl_con"
+DETAIL:  Key (f1)=(1) conflicts with existing key (f1)=(1).
+BEGIN;
+INSERT INTO deferred_excl VALUES(2); -- no fail here
+COMMIT; -- should fail here
+ERROR:  conflicting key value violates exclusion constraint "deferred_excl_con"
+DETAIL:  Key (f1)=(2) conflicts with existing key (f1)=(2).
+BEGIN;
+INSERT INTO deferred_excl VALUES(3);
+INSERT INTO deferred_excl VALUES(3); -- no fail here
+COMMIT; -- should fail here
+ERROR:  conflicting key value violates exclusion constraint "deferred_excl_con"
+DETAIL:  Key (f1)=(3) conflicts with existing key (f1)=(3).
+ALTER TABLE deferred_excl DROP CONSTRAINT deferred_excl_con;
+-- This should fail, but worth testing because of HOT updates
+UPDATE deferred_excl SET f1 = 3;
+ALTER TABLE deferred_excl ADD EXCLUDE (f1 WITH =);
+NOTICE:  ALTER TABLE / ADD EXCLUDE will create implicit index "deferred_excl_f1_exclusion" for table "deferred_excl"
+ERROR:  could not create exclusion constraint "deferred_excl_f1_exclusion"
+DETAIL:  Key (f1)=(3) conflicts with key (f1)=(3).
+DROP TABLE deferred_excl;