diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index cfecaa6931a9352f909966e021555b92e984fc6f..2063812942faa4b95b576703f4a9b3be3d428a90 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -218,6 +218,11 @@
       <entry>functions and procedures</entry>
      </row>
 
+     <row>
+      <entry><link linkend="catalog-pg-range"><structname>pg_range</structname></link></entry>
+      <entry>information about range types</entry>
+     </row>
+
      <row>
       <entry><link linkend="catalog-pg-rewrite"><structname>pg_rewrite</structname></link></entry>
       <entry>query rewrite rules</entry>
@@ -4594,6 +4599,78 @@
 
  </sect1>
 
+ <sect1 id="catalog-pg-range">
+  <title><structname>pg_range</structname></title>
+
+  <indexterm zone="catalog-pg-range">
+   <primary>pg_range</primary>
+  </indexterm>
+
+  <para>
+   The catalog <structname>pg_range</structname> stores information about range types.
+  </para>
+
+  <table>
+   <title><structname>pg_range</> Columns</title>
+
+   <tgroup cols="4">
+    <thead>
+     <row>
+      <entry>Name</entry>
+      <entry>Type</entry>
+      <entry>References</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+
+    <tbody>
+     <row>
+      <entry><structfield>rngtypid</structfield></entry>
+      <entry><type>oid</type></entry>
+      <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry>
+      <entry>The type that is a range type</entry>
+     </row>
+
+     <row>
+      <entry><structfield>rngsubtype</structfield></entry>
+      <entry><type>oid</type></entry>
+      <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry>
+      <entry>Subtype of this range type, e.g. <type>integer</type> is the subtype of <type>int4range</type></entry>
+     </row>
+
+     <row>
+      <entry><structfield>rngcollation</structfield></entry>
+      <entry><type>oid</type></entry>
+      <entry><literal><link linkend="catalog-pg-collation"><structname>pg_collation</structname></link>.oid</literal></entry>
+      <entry>The collation used when comparing range boundaries</entry>
+     </row>
+
+     <row>
+      <entry><structfield>rngsubopc</structfield></entry>
+      <entry><type>oid</type></entry>
+      <entry><literal><link linkend="catalog-pg-opclass"><structname>pg_opclass</structname></link>.oid</literal></entry>
+      <entry>The operator class used when comparing range boundaries</entry>
+     </row>
+
+     <row>
+      <entry><structfield>rngcanonical</structfield></entry>
+      <entry><type>regproc</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry>A function to convert a range into its canonical form</entry>
+     </row>
+
+     <row>
+      <entry><structfield>rngsubdiff</structfield></entry>
+      <entry><type>regproc</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry>A function to return the distance between two lower and upper bound, as a <type>double precision</type>. Used for GiST support</entry>
+     </row>
+    </tbody>
+   </tgroup>
+  </table>
+
+ </sect1>
+
  <sect1 id="catalog-pg-rewrite">
   <title><structname>pg_rewrite</structname></title>
 
diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml
index e7b3098f28ffb1b7f66c64a59fbba2c7a9a28222..fe59a1c7763ee970508bbd2d09f5d5dcb28cfd1f 100644
--- a/doc/src/sgml/datatype.sgml
+++ b/doc/src/sgml/datatype.sgml
@@ -4173,6 +4173,8 @@ SET xmloption TO { DOCUMENT | CONTENT };
 
   &rowtypes;
 
+  &rangetypes;
+
   <sect1 id="datatype-oid">
    <title>Object Identifier Types</title>
 
@@ -4443,6 +4445,10 @@ SELECT * FROM pg_attribute
     <primary>anyenum</primary>
    </indexterm>
 
+   <indexterm zone="datatype-pseudo">
+    <primary>anyrange</primary>
+   </indexterm>
+
    <indexterm zone="datatype-pseudo">
     <primary>void</primary>
    </indexterm>
@@ -4519,6 +4525,13 @@ SELECT * FROM pg_attribute
         <xref linkend="datatype-enum">).</entry>
        </row>
 
+       <row>
+        <entry><type>anyrange</></entry>
+        <entry>Indicates that a function accepts any range data type
+        (see <xref linkend="extend-types-polymorphic"> and
+        <xref linkend="rangetypes">).</entry>
+       </row>
+
        <row>
         <entry><type>anynonarray</></entry>
         <entry>Indicates that a function accepts any non-array data type
@@ -4583,7 +4596,8 @@ SELECT * FROM pg_attribute
     only <type>void</> and <type>record</> as a result type (plus
     <type>trigger</> when the function is used as a trigger).  Some also
     support polymorphic functions using the types <type>anyarray</>,
-    <type>anyelement</>, <type>anyenum</>, and <type>anynonarray</>.
+    <type>anyelement</>, <type>anyenum</>, <type>anyrange</>, and
+    <type>anynonarray</>.
    </para>
 
    <para>
diff --git a/doc/src/sgml/extend.sgml b/doc/src/sgml/extend.sgml
index 7079db3ed3f7be7f55bef76b5fae4b9958f4c4aa..f3850b391e0e5244a19a567e8c172bb3e27b297d 100644
--- a/doc/src/sgml/extend.sgml
+++ b/doc/src/sgml/extend.sgml
@@ -198,14 +198,15 @@
    </indexterm>
 
     <para>
-     Four pseudo-types of special interest are <type>anyelement</>,
-     <type>anyarray</>, <type>anynonarray</>, and <type>anyenum</>,
-     which are collectively called <firstterm>polymorphic types</>.
-     Any function declared using these types is said to be
-     a <firstterm>polymorphic function</>.  A polymorphic function can
-     operate on many different data types, with the specific data type(s)
-     being determined by the data types actually passed to it in a particular
-     call.
+     Five pseudo-types of special interest are <type>anyelement</>,
+     <type>anyarray</>, <type>anynonarray</>, <type>anyenum</>,
+     and <type>anyrange</>, which are collectively
+     called <firstterm>polymorphic types</>.  Any function declared
+     using these types is said to be a <firstterm>polymorphic
+     function</>.  A polymorphic function can operate on many
+     different data types, with the specific data type(s) being
+     determined by the data types actually passed to it in a
+     particular call.
     </para>
 
     <para>
@@ -221,6 +222,11 @@
      <type>anyelement</type>, the actual array type in the
      <type>anyarray</type> positions must be an array whose elements are
      the same type appearing in the <type>anyelement</type> positions.
+     Similarly, if there are positions declared <type>anyrange</type>
+     and others declared
+     <type>anyelement</type>, the actual range type in the
+     <type>anyrange</type> positions must be a range whose subtype is
+     the same type appearing in the <type>anyelement</type> positions.
      <type>anynonarray</> is treated exactly the same as <type>anyelement</>,
      but adds the additional constraint that the actual type must not be
      an array type.
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index ed39e0b66126a7a747103ea30787afee51253e6e..fb69415f8000d65cb5757e3c0f788ce82f5f27cc 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -25,6 +25,7 @@
 <!ENTITY mvcc       SYSTEM "mvcc.sgml">
 <!ENTITY perform    SYSTEM "perform.sgml">
 <!ENTITY queries    SYSTEM "queries.sgml">
+<!entity rangetypes SYSTEM "rangetypes.sgml">
 <!ENTITY rowtypes   SYSTEM "rowtypes.sgml">
 <!ENTITY syntax     SYSTEM "syntax.sgml">
 <!ENTITY textsearch SYSTEM "textsearch.sgml">
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 2b8298c3e0cbadf73c9bb2a221f2d2f1f8b77999..f81bb9db9770422bf7dcb5c032ef2c1fdb5a51a6 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -10457,6 +10457,310 @@ SELECT NULLIF(value, '(none)') ...
    </para>
   </sect1>
 
+ <sect1 id="functions-range">
+  <title>Range Functions and Operators</title>
+
+  <para>
+   <xref linkend="range-operators-table"> shows the operators
+   available for range types.
+  </para>
+
+    <table id="range-operators-table">
+     <title>Range Operators</title>
+     <tgroup cols="4">
+      <thead>
+       <row>
+        <entry>Operator</entry>
+        <entry>Description</entry>
+        <entry>Example</entry>
+        <entry>Result</entry>
+       </row>
+      </thead>
+      <tbody>
+       <row>
+        <entry> <literal>=</literal> </entry>
+        <entry>equal</entry>
+        <entry><literal>int4range(1,5) = '[1,4]'::int4range</literal></entry>
+        <entry><literal>t</literal></entry>
+       </row>
+
+       <row>
+        <entry> <literal>&lt;&gt;</literal> </entry>
+        <entry>not equal</entry>
+        <entry><literal>numrange(1.1,2.2) &lt;&gt; numrange(1.1,2.3)</literal></entry>
+        <entry><literal>t</literal></entry>
+       </row>
+
+       <row>
+        <entry> <literal>&lt;</literal> </entry>
+        <entry>less than</entry>
+        <entry><literal>int4range(1,10) &lt; int4range(2,3)</literal></entry>
+        <entry><literal>t</literal></entry>
+       </row>
+
+       <row>
+        <entry> <literal>&gt;</literal> </entry>
+        <entry>greater than</entry>
+        <entry><literal>int4range(1,10) &gt; int4range(1,5)</literal></entry>
+        <entry><literal>t</literal></entry>
+       </row>
+
+       <row>
+        <entry> <literal>&lt;=</literal> </entry>
+        <entry>less than or equal</entry>
+        <entry><literal>numrange(1.1,2.2) &lt;= numrange(1.1,2.2)</literal></entry>
+        <entry><literal>t</literal></entry>
+       </row>
+
+       <row>
+        <entry> <literal>&gt;=</literal> </entry>
+        <entry>greater than or equal</entry>
+        <entry><literal>numrange(1.1,2.2) &gt;= numrange(1.1,2.0)</literal></entry>
+        <entry><literal>t</literal></entry>
+       </row>
+
+       <row>
+        <entry> <literal>@&gt;</literal> </entry>
+        <entry>contains</entry>
+        <entry><literal>'[2011-01-01,2011-03-01)'::tsrange @&gt; '2011-01-10'::timestamp</literal></entry>
+        <entry><literal>t</literal></entry>
+       </row>
+
+       <row>
+        <entry> <literal>&lt;@</literal> </entry>
+        <entry>is contained by</entry>
+        <entry><literal>int4range(2,4) &lt;@ int4range(1,7)</literal></entry>
+        <entry><literal>t</literal></entry>
+       </row>
+
+       <row>
+        <entry> <literal>&amp;&amp;</literal> </entry>
+        <entry>overlap (have points in common)</entry>
+        <entry><literal>int8range(3,7) &amp;&amp; int8range(4,12)</literal></entry>
+        <entry><literal>t</literal></entry>
+       </row>
+
+       <row>
+        <entry> <literal>&lt;&lt;</literal> </entry>
+        <entry>strictly left of</entry>
+        <entry><literal>int8range(1,10) &lt;&lt; int8range(100,110)</literal></entry>
+        <entry><literal>t</literal></entry>
+       </row>
+
+       <row>
+        <entry> <literal>&gt;&gt;</literal> </entry>
+        <entry>strictly right of</entry>
+        <entry><literal>int8range(50,60) &gt;&gt; int8range(20,30)</literal></entry>
+        <entry><literal>t</literal></entry>
+       </row>
+
+       <row>
+        <entry> <literal>&amp;&lt;</literal> </entry>
+        <entry>Does not extend to the right of?</entry>
+        <entry><literal>int8range(1,20) &amp;&lt; int8range(18,20)</literal></entry>
+        <entry><literal>t</literal></entry>
+       </row>
+
+       <row>
+        <entry> <literal>&amp;&gt;</literal> </entry>
+        <entry>Does not extend to the left of?</entry>
+        <entry><literal>int8range(7,20) &amp;&gt; int8range(5,10)</literal></entry>
+        <entry><literal>t</literal></entry>
+       </row>
+
+       <row>
+        <entry> <literal>-|-</literal> </entry>
+        <entry>adjacent?</entry>
+        <entry><literal>numrange(1.1,2.2) -|- numrange(2.2,3.3)</literal></entry>
+        <entry><literal>t</literal></entry>
+       </row>
+
+       <row>
+        <entry> <literal>+</literal> </entry>
+        <entry>Union</entry>
+        <entry><literal>numrange(5,15) + numrange(10,20)</literal></entry>
+        <entry><literal>[5,20)</literal></entry>
+       </row>
+
+       <row>
+        <entry> <literal>-</literal> </entry>
+        <entry>Difference</entry>
+        <entry><literal>int8range(5,15) - int8range(10,20)</literal></entry>
+        <entry><literal>[5,10)</literal></entry>
+       </row>
+
+       <row>
+        <entry> <literal>*</literal> </entry>
+        <entry>Intersection</entry>
+        <entry><literal>int8range(5,15) * int8range(10,20)</literal></entry>
+        <entry><literal>[10,15)</literal></entry>
+       </row>
+
+       <row>
+        <entry> <literal>!?</literal> </entry>
+        <entry>Is empty?</entry>
+        <entry><literal>'empty'::int4range !?</literal></entry>
+        <entry><literal>t</literal></entry>
+       </row>
+
+       <row>
+        <entry> <literal>?</literal> </entry>
+        <entry>Is non-empty?</entry>
+        <entry><literal>numrange(1.0,2.0)?</literal></entry>
+        <entry><literal>t</literal></entry>
+       </row>
+      </tbody>
+     </tgroup>
+    </table>
+
+  <para>
+   Range comparisons compare the lower bounds first, and only if
+   equal, compare the upper bounds. This is generally most useful for
+   B-tree indexes, rather than being useful comparisons by themselves.
+  </para>
+
+  <para>
+   See <xref linkend="rangetypes"> for more details about range operator
+   behavior.
+  </para>
+
+  <para>
+   <xref linkend="range-functions-table"> shows the functions
+   available for use with range types. See <xref linkend="rangetypes">
+   for more information  and examples of the use of these functions.
+  </para>
+
+  <indexterm>
+    <primary>lower</primary>
+  </indexterm>
+  <indexterm>
+    <primary>upper</primary>
+  </indexterm>
+  <indexterm>
+    <primary>empty</primary>
+  </indexterm>
+  <indexterm>
+    <primary>non_empty</primary>
+  </indexterm>
+  <indexterm>
+    <primary>lower_inc</primary>
+  </indexterm>
+  <indexterm>
+    <primary>upper_inc</primary>
+  </indexterm>
+  <indexterm>
+    <primary>lower_inf</primary>
+  </indexterm>
+  <indexterm>
+    <primary>upper_inf</primary>
+  </indexterm>
+
+    <table id="range-functions-table">
+     <title>Range Functions</title>
+     <tgroup cols="5">
+      <thead>
+       <row>
+        <entry>Function</entry>
+        <entry>Return Type</entry>
+        <entry>Description</entry>
+        <entry>Example</entry>
+        <entry>Result</entry>
+       </row>
+      </thead>
+      <tbody>
+       <row>
+        <entry>
+         <literal>
+          <function>lower</function>(<type>anyrange</type>)
+         </literal>
+        </entry>
+        <entry><type>anyrange</type></entry>
+        <entry>lower bound of range</entry>
+        <entry><literal>lower(numrange(1.1,2.2))</literal></entry>
+        <entry><literal>1.1</literal></entry>
+       </row>
+       <row>
+        <entry>
+         <literal>
+          <function>upper</function>(<type>anyrange</type>)
+         </literal>
+        </entry>
+        <entry><type>anyrange</type></entry>
+        <entry>upper bound of range</entry>
+        <entry><literal>upper(numrange(1.1,2.2))</literal></entry>
+        <entry><literal>2.2</literal></entry>
+       </row>
+       <row>
+        <entry>
+         <literal>
+          <function>empty</function>(<type>anyrange</type>)
+         </literal>
+        </entry>
+        <entry><type>anyrange</type></entry>
+        <entry>is the range empty?</entry>
+        <entry><literal>empty(numrange(1.1,2.2))</literal></entry>
+        <entry><literal>false</literal></entry>
+       </row>
+       <row>
+        <entry>
+         <literal>
+          <function>non_empty</function>(<type>anyrange</type>)
+         </literal>
+        </entry>
+        <entry><type>anyrange</type></entry>
+        <entry>is the range non-empty?</entry>
+        <entry><literal>non_empty(numrange(1.1,2.2))</literal></entry>
+        <entry><literal>true</literal></entry>
+       </row>
+       <row>
+        <entry>
+         <literal>
+          <function>lower_inc</function>(<type>anyrange</type>)
+         </literal>
+        </entry>
+        <entry><type>anyrange</type></entry>
+        <entry>is the lower bound of the range inclusive?</entry>
+        <entry><literal>lower_inc(numrange(1.1,2.2))</literal></entry>
+        <entry><literal>true</literal></entry>
+       </row>
+       <row>
+        <entry>
+         <literal>
+          <function>upper_inc</function>(<type>anyrange</type>)
+         </literal>
+        </entry>
+        <entry><type>anyrange</type></entry>
+        <entry>is the upper bound of the range inclusive?</entry>
+        <entry><literal>upper_inc(numrange(1.1,2.2))</literal></entry>
+        <entry><literal>false</literal></entry>
+       </row>
+       <row>
+        <entry>
+         <literal>
+          <function>lower_inf</function>(<type>anyrange</type>)
+         </literal>
+        </entry>
+        <entry><type>anyrange</type></entry>
+        <entry>is the lower bound of the range infinite?</entry>
+        <entry><literal>lower_inf('(,)'::daterange)</literal></entry>
+        <entry><literal>true</literal></entry>
+       </row>
+       <row>
+        <entry>
+         <literal>
+          <function>upper_inf</function>(<type>anyrange</type>)
+         </literal>
+        </entry>
+        <entry><type>anyrange</type></entry>
+        <entry>is the upper bound of the range infinite?</entry>
+        <entry><literal>upper_inf('(,)'::daterange)</literal></entry>
+        <entry><literal>true</literal></entry>
+       </row>
+      </tbody>
+     </tgroup>
+    </table>
+  </sect1>
+
  <sect1 id="functions-aggregate">
   <title>Aggregate Functions</title>
 
diff --git a/doc/src/sgml/plpgsql.sgml b/doc/src/sgml/plpgsql.sgml
index 19c15ad26fc3d204b52b007babbcfdbaadb075c7..f33cef55ed0e81c6b8e85df718bac143006587dd 100644
--- a/doc/src/sgml/plpgsql.sgml
+++ b/doc/src/sgml/plpgsql.sgml
@@ -139,7 +139,7 @@
      <application>PL/pgSQL</> functions can also be declared to accept
      and return the polymorphic types
      <type>anyelement</type>, <type>anyarray</type>, <type>anynonarray</type>,
-     and <type>anyenum</>.  The actual
+     <type>anyenum</>, and <type>anyrange</type>.  The actual
      data types handled by a polymorphic function can vary from call to
      call, as discussed in <xref linkend="extend-types-polymorphic">.
      An example is shown in <xref linkend="plpgsql-declaration-parameters">.
@@ -500,8 +500,8 @@ $$ LANGUAGE plpgsql;
      <para>
       When the return type of a <application>PL/pgSQL</application>
       function is declared as a polymorphic type (<type>anyelement</type>,
-      <type>anyarray</type>, <type>anynonarray</type>, or <type>anyenum</>),
-      a special parameter <literal>$0</literal>
+      <type>anyarray</type>, <type>anynonarray</type>, <type>anyenum</type>,
+      or <type>anyrange</type>), a special parameter <literal>$0</literal>
       is created.  Its data type is the actual return type of the function,
       as deduced from the actual input types (see <xref
       linkend="extend-types-polymorphic">).
diff --git a/doc/src/sgml/rangetypes.sgml b/doc/src/sgml/rangetypes.sgml
new file mode 100644
index 0000000000000000000000000000000000000000..fc5896d8f4245cafbfaf25a77568c8275b16cc79
--- /dev/null
+++ b/doc/src/sgml/rangetypes.sgml
@@ -0,0 +1,373 @@
+<!-- doc/src/sgml/rangetypes.sgml -->
+
+<sect1 id="rangetypes">
+ <title>Range Types</title>
+
+ <indexterm>
+  <primary>range type</primary>
+ </indexterm>
+
+ <para>
+  Range types are data types representing a range of values over some
+  sub-type with a total order. For instance, ranges
+  of <type>timestamp</type> might be used to represent the ranges of
+  time that a meeting room is reserved. In this case the data type
+  is <type>tsrange</type> (short for "timestamp range"),
+  and <type>timestamp</type> is the sub-type with a total order.
+ </para>
+
+ <para>
+  Range types are useful because they represent many points in a
+  single value. The use of time and date ranges for scheduling
+  purposes is the clearest example; but price ranges, measurement
+  ranges from an instrument, etc., are also useful.
+ </para>
+
+ <sect2 id="rangetypes-builtin">
+  <title>Built-in Range Types</title>
+ <para>
+  PostgreSQL comes with the following built-in range types:
+  <itemizedlist>
+    <listitem>
+      <para>
+       <type>INT4RANGE</type> -- Range of <type>INTEGER</type>. This is a discrete range type, see <xref linkend="rangetypes-discrete">.
+      </para>
+    </listitem>
+    <listitem>
+      <para>
+       <type>INT8RANGE</type> -- Range of <type>BIGINT</type>. This is a discrete range type, see <xref linkend="rangetypes-discrete">.
+      </para>
+    </listitem>
+    <listitem>
+      <para>
+       <type>NUMRANGE</type> -- Range of <type>NUMERIC</type>.
+      </para>
+    </listitem>
+    <listitem>
+      <para>
+       <type>TSRANGE</type> -- Range of <type>TIMESTAMP WITHOUT TIME ZONE</type>.
+      </para>
+    </listitem>
+    <listitem>
+      <para>
+       <type>TSTZRANGE</type> -- Range of <type>TIMESTAMP WITH TIME ZONE</type>.
+      </para>
+    </listitem>
+    <listitem>
+      <para>
+       <type>DATERANGE</type> -- Range of <type>DATE</type>. This is a discrete range type, see <xref linkend="rangetypes-discrete">.
+      </para>
+    </listitem>
+  </itemizedlist>
+  In addition, you can define your own; see <xref linkend="SQL-CREATETYPE"> for more information.
+ </para>
+ </sect2>
+
+ <sect2 id="rangetypes-examples">
+  <title>Examples</title>
+  <para>
+<programlisting>
+CREATE TABLE reservation ( during TSRANGE );
+INSERT INTO  reservation VALUES
+  ( '[2010-01-01 14:30, 2010-01-01 15:30)' );
+
+-- Containment
+SELECT int4range(10, 20) @> 3;
+
+-- Overlaps
+SELECT numrange(11.1, 22.2) && numrange(20.0, 30.0);
+
+-- Find the upper bound:
+SELECT upper(int8range(15, 25));
+
+-- Compute the intersection:
+SELECT int4range(10, 20) * int4range(15, 25);
+
+-- Is the range non-empty?
+SELECT numrange(1, 5)? ;
+
+</programlisting>
+
+  See <xref linkend="range-functions-table">
+  and <xref linkend="range-operators-table"> for complete lists of
+  functions and operators on range types.
+  </para>
+ </sect2>
+
+ <sect2 id="rangetypes-inclusivity">
+  <title>Inclusive and Exclusive Bounds</title>
+  <para>
+  Every range has two bounds, the lower bound and the upper bound. All
+  points in between those values are included in the range. An
+  inclusive bound means that the boundary point itself is included in
+  the range as well, while an exclusive bound means that the boundary
+  point is not included in the range.
+  </para>
+  <para>
+  An inclusive lower bound is represented by <literal>[</literal>
+  while an exclusive lower bound is represented
+  by <literal>(</literal> (see <xref linkend="rangetypes-construct">
+  and <xref linkend="rangetypes-io"> below). Likewise, an inclusive
+  upper bound is represented by <literal>]</literal>, while an
+  exclusive upper bound is represented by <literal>)</literal>.
+  </para>
+  <para>
+  Functions <literal>lower_inc</literal>
+  and <literal>upper_inc</literal> test the inclusivity of the lower
+  and upper bounds of a range, respectively.
+  </para>
+ </sect2>
+
+ <sect2 id="rangetypes-infinite">
+  <title>Infinite (unbounded) Ranges</title>
+  <para>
+  The lower bound of a range can be omitted, meaning that all points
+  less (or equal to, if inclusive) than the upper bound are included
+  in the range. Likewise, if the upper bound of the range is omitted,
+  then all points greater than (or equal to, if omitted) the lower
+  bound are included in the range. If both lower and upper bounds are
+  omitted, all points are considered to be in the range.
+  </para>
+  <para>
+  Functions <literal>lower_inf</literal>
+  and <literal>upper_inf</literal> test the range for infinite lower
+  and upper bounds of a range, respectively.
+  </para>
+ </sect2>
+
+ <sect2 id="rangetypes-io">
+  <title>Input/Output</title>
+  <para>
+  The input follows one of the following patterns:
+<synopsis>
+(<replaceable>lower-bound</replaceable>,<replaceable>upper-bound</replaceable>)
+(<replaceable>lower-bound</replaceable>,<replaceable>upper-bound</replaceable>]
+[<replaceable>lower-bound</replaceable>,<replaceable>upper-bound</replaceable>)
+[<replaceable>lower-bound</replaceable>,<replaceable>upper-bound</replaceable>]
+empty
+</synopsis>
+  Notice that the final pattern is <literal>empty</literal>, which
+  represents an empty range (a range that contains no points).
+  </para>
+  <para>
+  The <replaceable>lower-bound</replaceable> may be either a string
+  that is valid input for the sub-type, or omitted (to indicate no
+  lower bound); and <replaceable>upper-bound</replaceable> may be
+  either a string that is valid input for the sub-type, or omitted (to
+  indicate no upper bound).
+  </para>
+  <para>
+  Either the <replaceable>lower-bound</replaceable> or
+  the <replaceable>upper-bound</replaceable> may be quoted
+  using <literal>""</literal> (double quotation marks), which will allow
+  special characters such as "<literal>,</literal>". Within quotation
+  marks, "<literal>\</literal>" (backslash) serves as an escape
+  character.
+  </para>
+  <para>
+  The choice between the other input formats affects the inclusivity
+  of the bounds. See <xref linkend="rangetypes-inclusivity">.
+  </para>
+  <para>
+  Examples:
+<programlisting>
+-- includes point 3, does not include point 7, and does include all points in between
+select '[3,7)'
+
+-- does not include either 3 or 7, but includes all points in between
+select '(3,7)'
+
+-- includes only the single point 4
+select '[4,4]'
+</programlisting>
+  </para>
+ </sect2>
+
+ <sect2 id="rangetypes-construct">
+  <title>Constructing Ranges</title>
+  <para>
+   Each range type has a constructor by the same name. The constructor
+   accepts from zero to three arguments. The zero-argument form
+   constructs an empty range; the one-argument form constructs a
+   singleton range; the two-argument form constructs a range
+   in <literal>[ )</literal> form; and the three-argument form
+   constructs a range in a form specified by the third argument. For
+   example:
+<programlisting>
+-- Three-argument form: lower bound, upper bound, and third argument indicating
+-- inclusivity/exclusivity of bounds (if omitted, defaults to <literal>'[)'</literal>).
+SELECT numrange(1.0, 14.0, '(]');
+
+-- The int4range input will exclude the lower bound and include the upper bound; but the
+-- resulting output will appear in the canonical form; see <xref linkend="rangetypes-discrete">.
+SELECT int8range(1, 14, '(]');
+
+-- Single argument form constructs a singleton range; that is a range consisting of just
+-- one point.
+SELECT numrange(11.1);
+
+-- Zero-argument form constructs and empty range.
+SELECT numrange();
+
+-- Using NULL for a bound causes the range to be unbounded on that side; that is, negative
+-- infinity for the lower bound or positive infinity for the upper bound.
+SELECT numrange(NULL,2.2);
+</programlisting>
+  </para>
+ </sect2>
+
+ <sect2 id="rangetypes-discrete">
+  <title>Discrete Range Types</title>
+  <para>
+  Discrete ranges are those that have a
+  defined <literal>canonical</literal> function. Loosely speaking, a
+  discrete range has a sub-type with a well-defined "step";
+  e.g. <type>INTEGER</type> or <type>DATE</type>.
+  </para>
+  <para>
+  The <literal>canonical</literal> function should take an input range
+  value, and return an equal range value that may have a different
+  formatting. For instance, the integer range <literal>[1,
+  7]</literal> could be represented by the equal integer
+  range <literal>[1, 8)</literal>. The two values are equal because
+  there are no points within the integer domain
+  between <literal>7</literal> and <literal>8</literal>, so not
+  including the end point <literal>8</literal> is the same as
+  including the end point <literal>7</literal>. The canonical output
+  for two values that are equal, like <literal>[1, 7]</literal>
+  and <literal>[1, 8)</literal>, must be equal. It doesn't matter
+  which representation you choose to be the canonical one, as long as
+  two equal values with different formattings are always mapped to the
+  same value with the same formatting. If the canonical function is
+  not specified, then ranges with different formatting
+  (e.g. <literal>[1, 7]</literal> and <literal>[1, 8)</literal>) will
+  always be treated as unequal.
+  </para>
+  <para>
+  For types such as <type>NUMRANGE</type>, this is not possible,
+  because there are always points in between two
+  distinct <type>NUMERIC</type> values.
+  </para>
+  <para>
+  The built-in range
+  types <type>INT4RANGE</type>, <type>INT8RANGE</type>,
+  and <type>DATERNAGE</type> all use a canonical form that includes
+  the lower bound and excludes the upper bound; that is, <literal>[
+  )</literal>. User-defined ranges can use other conventions, however.
+  </para>
+ </sect2>
+
+ <sect2 id="rangetypes-defining">
+  <title>Defining New Range Types</title>
+  <para>
+  Users can define their own range types. The most common reason to do
+  this is to use ranges where the subtype is not among the built-in
+  range types, e.g. a range of type <type>FLOAT</type> (or, if the
+  subtype itself is a user-defined type).
+  </para>
+  <para>
+  For example: to define a new range type of sub-type <type>DOUBLE PRECISION</type>:
+<programlisting>
+CREATE TYPE FLOATRANGE AS RANGE (
+  SUBTYPE = DOUBLE PRECISION
+);
+
+SELECT '[1.234, 5.678]'::floatrange;
+</programlisting>
+  Because <type>DOUBLE PRECISION</type> has no meaningful "step", we
+  do not define a <literal>canonical</literal>
+  function. See <xref linkend="SQL-CREATETYPE"> for more
+  information.
+  </para>
+  <para>
+  Defining your own range type also allows you to specify a different
+  operator class or collation to use (which affects the points that
+  fall between the range boundaries), or a different canonicalization
+  function.
+  </para>
+ </sect2>
+
+ <sect2 id="rangetypes-gist">
+  <indexterm>
+    <primary>range type</primary>
+    <secondary>gist</secondary>
+  </indexterm>
+  <title>Indexing</title>
+  <para>
+   GiST indexes can be applied to a table containing a range type. For instance:
+<programlisting>
+CREATE INDEX reservation_idx ON reservation USING gist (during);
+</programlisting>
+  This index may speed up queries
+  involving <literal>&amp;&amp;</literal>
+  (overlaps), <literal>@&gt;</literal> (contains), and all the boolean
+  operators found in this
+  table: <xref linkend="range-operators-table">.
+  </para>
+ </sect2>
+
+ <sect2 id="rangetypes-constraint">
+  <indexterm>
+    <primary>range type</primary>
+    <secondary>exclude</secondary>
+  </indexterm>
+  <title>Constraints on Ranges</title>
+  <para>
+   While <literal>UNIQUE</literal> is a natural constraint for scalar
+   values, it is usually unsuitable for range types. Instead, an
+   exclusion constraint is often more appropriate
+   (see <link linkend="SQL-CREATETABLE-EXCLUDE">CREATE TABLE
+   ... CONSTRAINT ... EXCLUDE</link>). Exclusion constraints allow the
+   specification of constraints such as "non-overlapping" on a range
+   type. For example:
+<programlisting>
+ALTER TABLE reservation
+  ADD EXCLUDE USING gist (during WITH &&);
+</programlisting>
+   That constraint will prevent any overlapping values from existing
+   in the table at the same time:
+<programlisting>
+INSERT INTO  reservation VALUES
+  ( '[2010-01-01 11:30, 2010-01-01 13:00)' );
+-- Result: INSERT 0 1
+INSERT INTO  reservation VALUES
+  ( '[2010-01-01 14:45, 2010-01-01 15:45)' );
+-- Result:
+--   ERROR:  conflicting key value violates exclusion constraint "reservation_during_excl"
+--   DETAIL:  Key (during)=([ 2010-01-01 14:45:00, 2010-01-01 15:45:00 )) conflicts with
+--            existing key (during)=([ 2010-01-01 14:30:00, 2010-01-01 15:30:00 )).
+</programlisting>
+  </para>
+  <para>
+   Combine range types and exclusion constraints
+   with <link linkend="btree-gist">btree_gist</link> for maximum
+   flexibility defining
+   constraints. After <literal>btree_gist</literal> is installed, the
+   following constraint will prevent overlapping ranges only if the
+   meeting room numbers are equal:
+<programlisting>
+
+CREATE TABLE room_reservation
+(
+  room TEXT,
+  during TSRANGE,
+  EXCLUDE USING gist (room WITH =, during WITH &&)
+);
+
+INSERT INTO room_reservation VALUES
+  ( '123A', '[2010-01-01 14:00, 2010-01-01 15:00)' );
+-- Result: INSERT 0 1
+INSERT INTO room_reservation VALUES
+  ( '123A', '[2010-01-01 14:30, 2010-01-01 15:30)' );
+-- Result:
+--   ERROR:  conflicting key value violates exclusion constraint "room_reservation_room_during_excl"
+--   DETAIL:  Key (room, during)=(123A, [ 2010-01-01 14:30:00, 2010-01-01 15:30:00 )) conflicts with
+--            existing key (room, during)=(123A, [ 2010-01-01 14:00:00, 2010-01-01 15:00:00 )).
+INSERT INTO room_reservation VALUES
+  ( '123B', '[2010-01-01 14:30, 2010-01-01 15:30)' );
+-- Result: INSERT 0 1
+
+</programlisting>
+  </para>
+ </sect2>
+</sect1>
diff --git a/doc/src/sgml/ref/create_type.sgml b/doc/src/sgml/ref/create_type.sgml
index ea45fadae697b5b7929c0c74f1992e82ab774e1d..ebcd461bd9149ca8c7c34c65d43f98ee2b6c5b52 100644
--- a/doc/src/sgml/ref/create_type.sgml
+++ b/doc/src/sgml/ref/create_type.sgml
@@ -27,6 +27,15 @@ CREATE TYPE <replaceable class="parameter">name</replaceable> AS
 CREATE TYPE <replaceable class="parameter">name</replaceable> AS ENUM
     ( [ '<replaceable class="parameter">label</replaceable>' [, ... ] ] )
 
+CREATE TYPE <replaceable class="parameter">name</replaceable> AS RANGE (
+    SUBTYPE = <replaceable class="parameter">subtype</replaceable>,
+    [ , SUBTYPE_OPCLASS = <replaceable class="parameter">subtype_operator_class</replaceable> ]
+    [ , SUBTYPE_DIFF = <replaceable class="parameter">subtype_diff_function</replaceable> ]
+    [ , CANONICAL = <replaceable class="parameter">canonical_function</replaceable> ]
+    [ , ANALYZE = <replaceable class="parameter">analyze_function</replaceable> ]
+    [ , COLLATION = <replaceable class="parameter">collation</replaceable> ]
+)
+
 CREATE TYPE <replaceable class="parameter">name</replaceable> (
     INPUT = <replaceable class="parameter">input_function</replaceable>,
     OUTPUT = <replaceable class="parameter">output_function</replaceable>
@@ -98,11 +107,61 @@ CREATE TYPE <replaceable class="parameter">name</replaceable>
    </para>
   </refsect2>
 
+  <refsect2 id="SQL-CREATETYPE-RANGE">
+   <title>Range Types</title>
+
+  <para>
+    The third form of <command>CREATE TYPE</command> creates a new
+    range type, as described in <xref linkend="rangetypes">.
+  </para>
+
+  <para>
+    The <replaceable class="parameter">subtype</replaceable> parameter
+    can be any type with an associated btree opclass (uses the type's
+    default btree operator class unless specified with
+    <replaceable class="parameter">subtype_operator_class</replaceable>).
+  </para>
+
+  <para>
+    The <replaceable class="parameter">subtype_diff</replaceable>
+    function takes two values of type
+    <replaceable class="parameter">subtype</replaceable> as argument, and
+    returns the distance between the two values as
+    <type>double precision</type>. This function is used for GiST indexing
+    (see <xref linkend="gist"> for more information), and should be provided
+    for efficiency.
+  </para>
+
+  <para>
+    The <replaceable class="parameter">canonical</replaceable>
+    function takes an argument and returns a value, both of the same
+    type being defined. This is used to convert the range value to a
+    canonical form, when applicable. See <xref linkend="rangetypes">
+    for more information. To define
+    a <replaceable class="parameter">canonical</replaceable> function,
+    you must first create a <firstterm>shell type</>, which is a
+    placeholder type that has no properties except a name and an
+    owner.  This is done by issuing the command <literal>CREATE TYPE
+   <replaceable>name</></literal>, with no additional parameters.
+  </para>
+
+  <para>
+    The <replaceable class="parameter">analyze</replaceable>
+    function is the same as for creating a base type.
+  </para>
+
+  <para>
+    The <replaceable class="parameter">collation</replaceable> option
+    specifies the collation used when determining the total order for
+    the range.
+  </para>
+  </refsect2>
+
   <refsect2>
    <title>Base Types</title>
 
   <para>
-   The third form of <command>CREATE TYPE</command> creates a new base type
+   The fourth form of <command>CREATE TYPE</command> creates a new base type
    (scalar type).  To create a new base type, you must be a superuser.
    (This restriction is made because an erroneous type definition could
    confuse or even crash the server.)
diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml
index 34e2cc29150534aa1005702a3c34eb0b443eb550..70643122046f1787574054807a6bf384b74c16da 100644
--- a/doc/src/sgml/xfunc.sgml
+++ b/doc/src/sgml/xfunc.sgml
@@ -997,8 +997,8 @@ $$ LANGUAGE SQL;
     <para>
      <acronym>SQL</acronym> functions can be declared to accept and
      return the polymorphic types <type>anyelement</type>,
-     <type>anyarray</type>, <type>anynonarray</type>, and
-     <type>anyenum</type>.  See <xref
+     <type>anyarray</type>, <type>anynonarray</type>,
+     <type>anyenum</type>, and <type>anyrange</type>.  See <xref
      linkend="extend-types-polymorphic"> for a more detailed
      explanation of polymorphic functions. Here is a polymorphic
      function <function>make_array</function> that builds up an array
@@ -3046,7 +3046,7 @@ CREATE OR REPLACE FUNCTION retcomposite(IN integer, IN integer,
      C-language functions can be declared to accept and
      return the polymorphic types
      <type>anyelement</type>, <type>anyarray</type>, <type>anynonarray</type>,
-     and <type>anyenum</type>.
+     <type>anyenum</type>, and <type>anyrange</type>.
      See <xref linkend="extend-types-polymorphic"> for a more detailed explanation
      of polymorphic functions. When function arguments or return types
      are defined as polymorphic types, the function author cannot know
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index 7e0b7d65a8749815475a0d625f508fbb5ccc2a3c..5a4419d3a80fc7754be99f36748a6862ed6196f0 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -13,8 +13,8 @@ include $(top_builddir)/src/Makefile.global
 OBJS = catalog.o dependency.o heap.o index.o indexing.o namespace.o aclchk.o \
        objectaddress.o pg_aggregate.o pg_collation.o pg_constraint.o pg_conversion.o \
        pg_depend.o pg_enum.o pg_inherits.o pg_largeobject.o pg_namespace.o \
-       pg_operator.o pg_proc.o pg_db_role_setting.o pg_shdepend.o pg_type.o \
-       storage.o toasting.o
+       pg_operator.o pg_proc.o pg_range.o pg_db_role_setting.o pg_shdepend.o \
+       pg_type.o storage.o toasting.o
 
 BKIFILES = postgres.bki postgres.description postgres.shdescription
 
@@ -39,7 +39,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
 	pg_ts_parser.h pg_ts_template.h pg_extension.h \
 	pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \
 	pg_foreign_table.h \
-	pg_default_acl.h pg_seclabel.h pg_shseclabel.h pg_collation.h \
+	pg_default_acl.h pg_seclabel.h pg_shseclabel.h pg_collation.h pg_range.h \
 	toasting.h indexing.h \
     )
 
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index 5f7d7f6b68b25956f225cf501b69e20765a8537c..8378c360b97f540821840b05e04e3f54acf3e056 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -91,8 +91,11 @@ ProcedureCreate(const char *procedureName,
 	int			parameterCount;
 	int			allParamCount;
 	Oid		   *allParams;
+	char	   *modes = NULL;
 	bool		genericInParam = false;
 	bool		genericOutParam = false;
+	bool		anyrangeInParam = false;
+	bool		anyrangeOutParam = false;
 	bool		internalInParam = false;
 	bool		internalOutParam = false;
 	Oid			variadicType = InvalidOid;
@@ -152,6 +155,24 @@ ProcedureCreate(const char *procedureName,
 		allParams = parameterTypes->values;
 	}
 
+	if (parameterModes != PointerGetDatum(NULL))
+	{
+		/*
+		 * We expect the array to be a 1-D CHAR array; verify that. We don't
+		 * need to use deconstruct_array() since the array data is just going
+		 * to look like a C array of char values.
+		 */
+		ArrayType  *modesArray = (ArrayType *) DatumGetPointer(parameterModes);
+
+		if (ARR_NDIM(modesArray) != 1 ||
+			ARR_DIMS(modesArray)[0] != allParamCount ||
+			ARR_HASNULL(modesArray) ||
+			ARR_ELEMTYPE(modesArray) != CHAROID)
+			elog(ERROR, "parameterModes is not a 1-D char array");
+		modes = (char *) ARR_DATA_PTR(modesArray);
+	}
+
+
 	/*
 	 * Do not allow polymorphic return type unless at least one input argument
 	 * is polymorphic.	Also, do not allow return type INTERNAL unless at
@@ -161,6 +182,9 @@ ProcedureCreate(const char *procedureName,
 	{
 		switch (parameterTypes->values[i])
 		{
+			case ANYRANGEOID:
+				anyrangeInParam = true;
+				/* FALL THROUGH */
 			case ANYARRAYOID:
 			case ANYELEMENTOID:
 			case ANYNONARRAYOID:
@@ -177,14 +201,17 @@ ProcedureCreate(const char *procedureName,
 	{
 		for (i = 0; i < allParamCount; i++)
 		{
-			/*
-			 * We don't bother to distinguish input and output params here, so
-			 * if there is, say, just an input INTERNAL param then we will
-			 * still set internalOutParam.	This is OK since we don't really
-			 * care.
-			 */
+			if (modes == NULL ||
+				(modes[i] != PROARGMODE_OUT &&
+				 modes[i] != PROARGMODE_INOUT &&
+				 modes[i] != PROARGMODE_TABLE))
+				 continue;
+
 			switch (allParams[i])
 			{
+				case ANYRANGEOID:
+					anyrangeOutParam = true;
+					/* FALL THROUGH */
 				case ANYARRAYOID:
 				case ANYELEMENTOID:
 				case ANYNONARRAYOID:
@@ -205,6 +232,13 @@ ProcedureCreate(const char *procedureName,
 				 errmsg("cannot determine result data type"),
 				 errdetail("A function returning a polymorphic type must have at least one polymorphic argument.")));
 
+	if ((returnType == ANYRANGEOID || anyrangeOutParam) &&
+		!anyrangeInParam)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+				 errmsg("cannot determine result data type"),
+				 errdetail("A function returning ANYRANGE must have at least one ANYRANGE argument.")));
+
 	if ((returnType == INTERNALOID || internalOutParam) && !internalInParam)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
@@ -225,23 +259,8 @@ ProcedureCreate(const char *procedureName,
 						procedureName,
 						format_type_be(parameterTypes->values[0]))));
 
-	if (parameterModes != PointerGetDatum(NULL))
+	if (modes != NULL)
 	{
-		/*
-		 * We expect the array to be a 1-D CHAR array; verify that. We don't
-		 * need to use deconstruct_array() since the array data is just going
-		 * to look like a C array of char values.
-		 */
-		ArrayType  *modesArray = (ArrayType *) DatumGetPointer(parameterModes);
-		char	   *modes;
-
-		if (ARR_NDIM(modesArray) != 1 ||
-			ARR_DIMS(modesArray)[0] != allParamCount ||
-			ARR_HASNULL(modesArray) ||
-			ARR_ELEMTYPE(modesArray) != CHAROID)
-			elog(ERROR, "parameterModes is not a 1-D char array");
-		modes = (char *) ARR_DATA_PTR(modesArray);
-
 		/*
 		 * Only the last input parameter can be variadic; if it is, save its
 		 * element type.  Errors here are just elog since caller should have
diff --git a/src/backend/catalog/pg_range.c b/src/backend/catalog/pg_range.c
new file mode 100644
index 0000000000000000000000000000000000000000..3b9003395e5b9875c39dfb2201fa4016ab46f70a
--- /dev/null
+++ b/src/backend/catalog/pg_range.c
@@ -0,0 +1,136 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_range.c
+ *	  routines to support manipulation of the pg_range relation
+ *
+ * Copyright (c) 2006-2010, PostgreSQL Global Development Group
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/catalog/pg_range.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/pg_collation.h"
+#include "catalog/pg_opclass.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_range.h"
+#include "catalog/pg_type.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/tqual.h"
+#include "utils/rel.h"
+
+/*
+ * RangeCreate
+ *		Create an entry in pg_range.
+ */
+void
+RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
+			Oid rangeSubOpclass, RegProcedure rangeCanonical,
+			RegProcedure rangeSubDiff)
+{
+	Relation			pg_range;
+	Datum				values[Natts_pg_range];
+	bool				nulls[Natts_pg_range];
+	HeapTuple			tup;
+	ObjectAddress		myself;
+	ObjectAddress		referenced;
+
+	pg_range = heap_open(RangeRelationId, RowExclusiveLock);
+
+	memset(nulls, 0, Natts_pg_range * sizeof(bool));
+
+	values[Anum_pg_range_rngtypid - 1]	   = ObjectIdGetDatum(rangeTypeOid);
+	values[Anum_pg_range_rngsubtype - 1]   = ObjectIdGetDatum(rangeSubType);
+	values[Anum_pg_range_rngcollation - 1] = ObjectIdGetDatum(rangeCollation);
+	values[Anum_pg_range_rngsubopc - 1]	   = ObjectIdGetDatum(rangeSubOpclass);
+	values[Anum_pg_range_rngcanonical - 1] = ObjectIdGetDatum(rangeCanonical);
+	values[Anum_pg_range_rngsubdiff - 1]  = ObjectIdGetDatum(rangeSubDiff);
+
+	tup = heap_form_tuple(RelationGetDescr(pg_range), values, nulls);
+	simple_heap_insert(pg_range, tup);
+	CatalogUpdateIndexes(pg_range, tup);
+	heap_freetuple(tup);
+
+	/* record dependencies */
+
+	myself.classId	   = TypeRelationId;
+	myself.objectId	   = rangeTypeOid;
+	myself.objectSubId = 0;
+
+	referenced.classId	   = TypeRelationId;
+	referenced.objectId	   = rangeSubType;
+	referenced.objectSubId = 0;
+	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+	referenced.classId	   = OperatorClassRelationId;
+	referenced.objectId	   = rangeSubOpclass;
+	referenced.objectSubId = 0;
+	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+	if (OidIsValid(rangeCollation))
+	{
+		referenced.classId	   = CollationRelationId;
+		referenced.objectId	   = rangeCollation;
+		referenced.objectSubId = 0;
+		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+	}
+
+	if (OidIsValid(rangeCanonical))
+	{
+		referenced.classId	   = ProcedureRelationId;
+		referenced.objectId	   = rangeCanonical;
+		referenced.objectSubId = 0;
+		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+	}
+
+	if (OidIsValid(rangeSubDiff))
+	{
+		referenced.classId	   = ProcedureRelationId;
+		referenced.objectId	   = rangeSubDiff;
+		referenced.objectSubId = 0;
+		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+	}
+
+	heap_close(pg_range, RowExclusiveLock);
+}
+
+
+/*
+ * RangeDelete
+ *		Remove the pg_range entry.
+ */
+void
+RangeDelete(Oid rangeTypeOid)
+{
+	Relation	pg_range;
+	ScanKeyData key[1];
+	SysScanDesc scan;
+	HeapTuple	tup;
+
+	pg_range = heap_open(RangeRelationId, RowExclusiveLock);
+
+	ScanKeyInit(&key[0],
+				Anum_pg_range_rngtypid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(rangeTypeOid));
+
+	scan = systable_beginscan(pg_range, RangeTypidIndexId, true,
+							  SnapshotNow, 1, key);
+
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		simple_heap_delete(pg_range, &tup->t_self);
+	}
+
+	systable_endscan(scan);
+
+	heap_close(pg_range, RowExclusiveLock);
+}
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 5069c5759ec60e730a70f98d2145dc78b200e156..91488bbbf5c9487381ea91a5aa8372dcb0fb03f1 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -42,7 +42,11 @@
 #include "catalog/pg_constraint.h"
 #include "catalog/pg_depend.h"
 #include "catalog/pg_enum.h"
+#include "catalog/pg_language.h"
 #include "catalog/pg_namespace.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_proc_fn.h"
+#include "catalog/pg_range.h"
 #include "catalog/pg_type.h"
 #include "catalog/pg_type_fn.h"
 #include "commands/defrem.h"
@@ -63,6 +67,7 @@
 #include "utils/fmgroids.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
+#include "utils/rangetypes.h"
 #include "utils/rel.h"
 #include "utils/syscache.h"
 #include "utils/tqual.h"
@@ -87,6 +92,9 @@ static Oid	findTypeSendFunction(List *procname, Oid typeOid);
 static Oid	findTypeTypmodinFunction(List *procname);
 static Oid	findTypeTypmodoutFunction(List *procname);
 static Oid	findTypeAnalyzeFunction(List *procname, Oid typeOid);
+static Oid  findRangeCanonicalFunction(List *procname, Oid typeOid);
+static Oid	findRangeSubOpclass(List *procname, Oid typeOid);
+static Oid findRangeSubtypeDiffFunction(List *procname, Oid typeOid);
 static void	validateDomainConstraint(Oid domainoid, char *ccbin);
 static List *get_rels_with_domain(Oid domainOid, LOCKMODE lockmode);
 static void checkDomainOwner(HeapTuple tup);
@@ -95,6 +103,8 @@ static char *domainAddConstraint(Oid domainOid, Oid domainNamespace,
 					Oid baseTypeOid,
 					int typMod, Constraint *constr,
 					char *domainName);
+static void makeRangeConstructor(char *name, Oid namespace, Oid rettype,
+								 Oid subtype);
 
 
 /*
@@ -643,6 +653,14 @@ RemoveTypeById(Oid typeOid)
 	if (((Form_pg_type) GETSTRUCT(tup))->typtype == TYPTYPE_ENUM)
 		EnumValuesDelete(typeOid);
 
+	/*
+	 * If it is a range type, delete the pg_range entries too; we
+	 * don't bother with making dependency entries for those, so it
+	 * has to be done "by hand" here.
+	 */
+	if (((Form_pg_type) GETSTRUCT(tup))->typtype == TYPTYPE_RANGE)
+		RangeDelete(typeOid);
+
 	ReleaseSysCache(tup);
 
 	heap_close(relation, RowExclusiveLock);
@@ -724,14 +742,15 @@ DefineDomain(CreateDomainStmt *stmt)
 	basetypeoid = HeapTupleGetOid(typeTup);
 
 	/*
-	 * Base type must be a plain base type, another domain or an enum. Domains
-	 * over pseudotypes would create a security hole.  Domains over composite
-	 * types might be made to work in the future, but not today.
+	 * Base type must be a plain base type, another domain, an enum or a range
+	 * type. Domains over pseudotypes would create a security hole.  Domains
+	 * over composite types might be made to work in the future, but not today.
 	 */
 	typtype = baseType->typtype;
 	if (typtype != TYPTYPE_BASE &&
 		typtype != TYPTYPE_DOMAIN &&
-		typtype != TYPTYPE_ENUM)
+		typtype != TYPTYPE_ENUM &&
+		typtype != TYPTYPE_RANGE)
 		ereport(ERROR,
 				(errcode(ERRCODE_DATATYPE_MISMATCH),
 				 errmsg("\"%s\" is not a valid base type for a domain",
@@ -1134,6 +1153,327 @@ DefineEnum(CreateEnumStmt *stmt)
 	pfree(enumArrayName);
 }
 
+/*
+ * DefineRange
+ *		Registers a new range type.
+ */
+void
+DefineRange(CreateRangeStmt *stmt)
+{
+	char		*typeName;
+	char		*rangeArrayName;
+	Oid			 typeNamespace;
+	Oid			 typoid;
+	Oid			 rangeArrayOid;
+	List		*parameters = stmt->params;
+
+	ListCell	*lc;
+	List		*rangeSubOpclassName  = NIL;
+	List		*rangeSubtypeDiffName = NIL;
+	List		*rangeCollationName	  = NIL;
+	Oid			 rangeCollation		  = InvalidOid;
+	regproc		 rangeAnalyze		  = InvalidOid;
+	Oid			 rangeSubtype		  = InvalidOid;
+	regproc		 rangeSubOpclass	  = InvalidOid;
+	regproc		 rangeCanonical		  = InvalidOid;
+	regproc		 rangeSubtypeDiff	  = InvalidOid;
+
+	AclResult	 aclresult;
+
+	/* Convert list of names to a name and namespace */
+	typeNamespace = QualifiedNameGetCreationNamespace(stmt->typeName,
+													  &typeName);
+
+	/* Check we have creation rights in target namespace */
+	aclresult = pg_namespace_aclcheck(typeNamespace, GetUserId(), ACL_CREATE);
+	if (aclresult != ACLCHECK_OK)
+		aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
+					   get_namespace_name(typeNamespace));
+
+	/*
+	 * Look to see if type already exists (presumably as a shell; if not,
+	 * TypeCreate will complain).
+	 */
+	typoid = GetSysCacheOid2(TYPENAMENSP,
+							 CStringGetDatum(typeName),
+							 ObjectIdGetDatum(typeNamespace));
+
+	/*
+	 * If it's not a shell, see if it's an autogenerated array type, and if so
+	 * rename it out of the way.
+	 */
+	if (OidIsValid(typoid) && get_typisdefined(typoid))
+	{
+		if (moveArrayTypeName(typoid, typeName, typeNamespace))
+			typoid = InvalidOid;
+	}
+
+	/*
+	 * If it doesn't exist, create it as a shell, so that the OID is known for
+	 * use in the I/O function definitions.
+	 */
+	if (!OidIsValid(typoid))
+	{
+		typoid = TypeShellMake(typeName, typeNamespace, GetUserId());
+		/* Make new shell type visible for modification below */
+		CommandCounterIncrement();
+
+		/*
+		 * If the command was a parameterless CREATE TYPE, we're done ---
+		 * creating the shell type was all we're supposed to do.
+		 */
+		if (parameters == NIL)
+			return;
+	}
+	else
+	{
+		/* Complain if dummy CREATE TYPE and entry already exists */
+		if (parameters == NIL)
+			ereport(ERROR,
+					(errcode(ERRCODE_DUPLICATE_OBJECT),
+					 errmsg("type \"%s\" already exists", typeName)));
+	}
+
+	foreach(lc, stmt->params)
+	{
+		DefElem *defel  = lfirst(lc);
+
+		if (pg_strcasecmp(defel->defname, "subtype") == 0)
+		{
+			if (OidIsValid(rangeSubtype))
+				ereport(ERROR,
+						(errcode(ERRCODE_SYNTAX_ERROR),
+						 errmsg("conflicting or redundant options")));
+			rangeSubtype = typenameTypeId(NULL, defGetTypeName(defel));
+		}
+		else if (pg_strcasecmp(defel->defname, "canonical") == 0)
+		{
+			if (OidIsValid(rangeCanonical))
+				ereport(ERROR,
+						(errcode(ERRCODE_SYNTAX_ERROR),
+						 errmsg("conflicting or redundant options")));
+			rangeCanonical = findRangeCanonicalFunction(
+				defGetQualifiedName(defel), typoid);
+		}
+		else if (pg_strcasecmp(defel->defname, "collation") == 0)
+		{
+			if (rangeCollationName != NIL)
+				ereport(ERROR,
+						(errcode(ERRCODE_SYNTAX_ERROR),
+						 errmsg("conflicting or redundant options")));
+			rangeCollationName = defGetQualifiedName(defel);
+		}
+		else if (pg_strcasecmp(defel->defname, "analyze") == 0)
+		{
+			if (OidIsValid(rangeAnalyze))
+				ereport(ERROR,
+						(errcode(ERRCODE_SYNTAX_ERROR),
+						 errmsg("conflicting or redundant options")));
+			rangeAnalyze = findTypeAnalyzeFunction(defGetQualifiedName(defel),
+												   typoid);
+		}
+		else if (pg_strcasecmp(defel->defname, "subtype_opclass") == 0)
+		{
+			if (rangeSubOpclassName != NIL)
+				ereport(ERROR,
+						(errcode(ERRCODE_SYNTAX_ERROR),
+						 errmsg("conflicting or redundant options")));
+			rangeSubOpclassName = defGetQualifiedName(defel);
+		}
+		else if (pg_strcasecmp(defel->defname, "subtype_diff") == 0)
+		{
+			if (rangeSubtypeDiffName != NIL)
+				ereport(ERROR,
+						(errcode(ERRCODE_SYNTAX_ERROR),
+						 errmsg("conflicting or redundant options")));
+			rangeSubtypeDiffName = defGetQualifiedName(defel);
+		}
+		else
+		{
+			ereport(ERROR,
+					(errcode(ERRCODE_SYNTAX_ERROR),
+					 errmsg("type attribute \"%s\" not recognized",
+							defel->defname)));
+			continue;
+		}
+	}
+
+	if (!OidIsValid(rangeSubtype))
+			ereport(ERROR,
+					(errcode(ERRCODE_SYNTAX_ERROR),
+					 errmsg("type attribute \"subtype\" is required")));
+
+	if (type_is_collatable(rangeSubtype))
+	{
+		if (rangeCollationName == NIL)
+			rangeCollation = get_typcollation(rangeSubtype);
+		else
+			rangeCollation = get_collation_oid(rangeCollationName, false);
+	}
+	else if (rangeCollationName != NIL)
+		ereport(ERROR,
+				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+				 errmsg("range collation provided but subtype does not support collation")));
+
+	rangeSubOpclass = findRangeSubOpclass(rangeSubOpclassName, rangeSubtype);
+
+	if (rangeSubtypeDiffName != NIL)
+		rangeSubtypeDiff = findRangeSubtypeDiffFunction(
+			rangeSubtypeDiffName, rangeSubtype);
+
+	rangeArrayOid = AssignTypeArrayOid();
+
+	/* Create the pg_type entry */
+	typoid =
+		TypeCreate(InvalidOid,	/* no predetermined type OID */
+				   typeName,	/* type name */
+				   typeNamespace,		/* namespace */
+				   InvalidOid,	/* relation oid (n/a here) */
+				   0,			/* relation kind (ditto) */
+				   GetUserId(), /* owner's ID */
+				   -1,			/* internal size */
+				   TYPTYPE_RANGE,	/* type-type (range type) */
+				   TYPCATEGORY_RANGE,	/* type-category (range type) */
+				   false,		/* range types are never preferred */
+				   DEFAULT_TYPDELIM,	/* array element delimiter */
+				   F_RANGE_IN,	/* input procedure */
+				   F_RANGE_OUT,	/* output procedure */
+				   F_RANGE_RECV, /* receive procedure */
+				   F_RANGE_SEND, /* send procedure */
+				   InvalidOid,	/* typmodin procedure - none */
+				   InvalidOid,	/* typmodout procedure - none */
+				   rangeAnalyze,	/* analyze procedure - default */
+				   InvalidOid,	/* element type ID */
+				   false,		/* this is not an array type */
+				   rangeArrayOid,	/* array type we are about to create */
+				   InvalidOid,	/* base type ID (only for domains) */
+				   NULL,		/* never a default type value */
+				   NULL,		/* binary default isn't sent either */
+				   false,		/* never passed by value */
+				   'i',			/* int alignment */
+				   'x',			/* TOAST strategy always plain */
+				   -1,			/* typMod (Domains only) */
+				   0,			/* Array dimensions of typbasetype */
+				   false,		/* Type NOT NULL */
+				   InvalidOid);	/* typcollation */
+
+	/* create the entry in pg_range */
+	RangeCreate(typoid, rangeSubtype, rangeCollation, rangeSubOpclass,
+				rangeCanonical, rangeSubtypeDiff);
+
+	/*
+	 * Create the array type that goes with it.
+	 */
+	rangeArrayName = makeArrayTypeName(typeName, typeNamespace);
+
+	TypeCreate(rangeArrayOid,	/* force assignment of this type OID */
+			   rangeArrayName,	/* type name */
+			   typeNamespace,	/* namespace */
+			   InvalidOid,		/* relation oid (n/a here) */
+			   0,				/* relation kind (ditto) */
+			   GetUserId(),		/* owner's ID */
+			   -1,				/* internal size (always varlena) */
+			   TYPTYPE_BASE,	/* type-type (base type) */
+			   TYPCATEGORY_ARRAY,		/* type-category (array) */
+			   false,			/* array types are never preferred */
+			   DEFAULT_TYPDELIM,	/* array element delimiter */
+			   F_ARRAY_IN,		/* input procedure */
+			   F_ARRAY_OUT,		/* output procedure */
+			   F_ARRAY_RECV,	/* receive procedure */
+			   F_ARRAY_SEND,	/* send procedure */
+			   InvalidOid,		/* typmodin procedure - none */
+			   InvalidOid,		/* typmodout procedure - none */
+			   InvalidOid,		/* analyze procedure - default */
+			   typoid,		/* element type ID */
+			   true,			/* yes this is an array type */
+			   InvalidOid,		/* no further array type */
+			   InvalidOid,		/* base type ID */
+			   NULL,			/* never a default type value */
+			   NULL,			/* binary default isn't sent either */
+			   false,			/* never passed by value */
+			   'i',				/* align 'i' */
+			   'x',				/* ARRAY is always toastable */
+			   -1,				/* typMod (Domains only) */
+			   0,				/* Array dimensions of typbasetype */
+			   false,			/* Type NOT NULL */
+			   InvalidOid);		/* typcollation */
+
+	pfree(rangeArrayName);
+
+	makeRangeConstructor(typeName, typeNamespace, typoid, rangeSubtype);
+}
+
+/*
+ * Because there may exist several range types over one subtype, the range type
+ * can't be determined from the subtype. This means that constructors can't be
+ * polymorphic, and so we must generate a new constructor for every range type
+ * defined.
+ *
+ * We actually define 4 functions with 0 through 3 arguments. This is just to
+ * offer more convenience for the user.
+ */
+static void
+makeRangeConstructor(char *name, Oid namespace, Oid rangeOid, Oid subtype)
+{
+	ObjectAddress		referenced;
+	Oid					constructorArgTypes[3];
+	int					i;
+
+	referenced.classId	   = TypeRelationId;
+	referenced.objectId	   = rangeOid;
+	referenced.objectSubId = 0;
+
+	constructorArgTypes[0] = subtype;
+	constructorArgTypes[1] = subtype;
+	constructorArgTypes[2] = TEXTOID;
+
+	for (i = 0; i < 4; i++)
+	{
+		oidvector		*constructorArgTypesVector;
+		ObjectAddress	 myself;
+		Oid				 procOid;
+		char			*prosrc[4] = { "range_constructor0",
+									   "range_constructor1",
+									   "range_constructor2",
+									   "range_constructor3"};
+
+		constructorArgTypesVector = buildoidvector(constructorArgTypes, i);
+
+		procOid = ProcedureCreate(
+			name, /* name */
+			namespace, /* namespace */
+			false, /* replace */
+			false, /* return set */
+			rangeOid, /* return type */
+			INTERNALlanguageId, /* language */
+			F_FMGR_INTERNAL_VALIDATOR, /* language validator */
+			prosrc[i], /* prosrc */
+			NULL, /* probin */
+			false, /* agg */
+			false, /* window */
+			false, /* security definer */
+			false, /* strict */
+			PROVOLATILE_IMMUTABLE, /* volatility */
+			constructorArgTypesVector, /* param types */
+			PointerGetDatum(NULL), /* allParameterTypes */
+			PointerGetDatum(NULL), /* parameterModes */
+			PointerGetDatum(NULL), /* parameterNames */
+			NIL, /* parameterDefaults */
+			PointerGetDatum(NULL), /* proconfig */
+			1.0, /* procost */
+			0.0); /* prorows */
+
+		/*
+		 * Make the constructor internally-dependent on the range type so that
+		 * the user doesn't have to treat them as separate objects.
+		 */
+		myself.classId	   = ProcedureRelationId;
+		myself.objectId	   = procOid;
+		myself.objectSubId = 0;
+		recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+	}
+}
+
 /*
  * AlterEnum
  *		Adds a new label to an existing enum.
@@ -1449,6 +1789,103 @@ findTypeAnalyzeFunction(List *procname, Oid typeOid)
 	return procOid;
 }
 
+/*
+ * Find named btree opclass for subtype, or default btree opclass if
+ * opcname is NIL. This will be used for comparing values of subtype.
+ */
+static Oid
+findRangeSubOpclass(List *opcname, Oid subtype)
+{
+	Oid			opcid;
+
+	if (opcname == NIL)
+	{
+		opcid = GetDefaultOpClass(subtype, BTREE_AM_OID);
+		if (!OidIsValid(opcid))
+		{
+			ereport(ERROR,
+					(errcode(ERRCODE_UNDEFINED_OBJECT),
+					 errmsg("data type %s has no default operator class for access method \"btree\"",
+							format_type_be(subtype)),
+					 errhint("You must specify an operator class for the data type or define a default operator class for the data type.")));
+		}
+		return opcid;
+	}
+
+	opcid = get_opclass_oid(BTREE_AM_OID, opcname, false);
+
+	return opcid;
+}
+
+/*
+ * Used to find a range's 'canonical' function.
+ */
+static Oid
+findRangeSubtypeDiffFunction(List *procname, Oid typeOid)
+{
+	Oid			argList[2];
+	Oid			procOid;
+
+	argList[0] = typeOid;
+	argList[1] = typeOid;
+
+	procOid = LookupFuncName(procname, 2, argList, true);
+
+	if (!OidIsValid(procOid))
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_FUNCTION),
+				 errmsg("function %s does not exist",
+						func_signature_string(procname, 2, NIL, argList))));
+
+	if (get_func_rettype(procOid) != FLOAT8OID)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+				 errmsg("range subtype diff function %s must return type \"float8\"",
+						func_signature_string(procname, 2, NIL, argList))));
+
+	if (func_volatile(procOid) != PROVOLATILE_IMMUTABLE)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+				 errmsg("range subtype diff function %s must be immutable",
+						func_signature_string(procname, 2, NIL, argList))));
+
+	return procOid;
+}
+
+/*
+ * Used to find a range's 'canonical' function.
+ */
+static Oid
+findRangeCanonicalFunction(List *procname, Oid typeOid)
+{
+	Oid			argList[1];
+	Oid			procOid;
+
+	argList[0] = typeOid;
+
+	procOid = LookupFuncName(procname, 1, argList, true);
+
+	if (!OidIsValid(procOid))
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_FUNCTION),
+				 errmsg("function %s does not exist",
+						func_signature_string(procname, 1, NIL, argList))));
+
+	if (get_func_rettype(procOid) != typeOid)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+				 errmsg("range canonical function %s must return range type",
+						NameListToString(procname))));
+
+	if (func_volatile(procOid) != PROVOLATILE_IMMUTABLE)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+				 errmsg("range canonical function %s must be immutable",
+						func_signature_string(procname, 1, NIL, argList))));
+
+	return procOid;
+}
+
 /*
  *	AssignTypeArrayOid
  *
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 398bc40c490a188a28a8657b01cb021b353e6c60..45ca5ec4c7aca0453f9f2dacd16ecd8aebdfc82c 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -1352,6 +1352,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
 	if (fn_typtype == TYPTYPE_BASE ||
 		fn_typtype == TYPTYPE_DOMAIN ||
 		fn_typtype == TYPTYPE_ENUM ||
+		fn_typtype == TYPTYPE_RANGE ||
 		rettype == VOIDOID)
 	{
 		/*
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 24ac5295f6077ca5eead5de510c8b8a1f0a55c92..63958c3afc6a6a0638ce62aa076254d0816fe8a7 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3055,6 +3055,17 @@ _copyCreateEnumStmt(CreateEnumStmt *from)
 	return newnode;
 }
 
+static CreateRangeStmt *
+_copyCreateRangeStmt(CreateRangeStmt *from)
+{
+	CreateRangeStmt *newnode = makeNode(CreateRangeStmt);
+
+	COPY_NODE_FIELD(typeName);
+	COPY_NODE_FIELD(params);
+
+	return newnode;
+}
+
 static AlterEnumStmt *
 _copyAlterEnumStmt(AlterEnumStmt *from)
 {
@@ -4297,6 +4308,9 @@ copyObject(void *from)
 		case T_CreateEnumStmt:
 			retval = _copyCreateEnumStmt(from);
 			break;
+		case T_CreateRangeStmt:
+			retval = _copyCreateRangeStmt(from);
+			break;
 		case T_AlterEnumStmt:
 			retval = _copyAlterEnumStmt(from);
 			break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 4052a9a1fc98a58e209cba9fadb1555b3ecc6349..f3a34a12e20e88d906568d6eb36683a1c0adc713 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1438,6 +1438,15 @@ _equalCreateEnumStmt(CreateEnumStmt *a, CreateEnumStmt *b)
 	return true;
 }
 
+static bool
+_equalCreateRangeStmt(CreateRangeStmt *a, CreateRangeStmt *b)
+{
+	COMPARE_NODE_FIELD(typeName);
+	COMPARE_NODE_FIELD(params);
+
+	return true;
+}
+
 static bool
 _equalAlterEnumStmt(AlterEnumStmt *a, AlterEnumStmt *b)
 {
@@ -2826,6 +2835,9 @@ equal(void *a, void *b)
 		case T_CreateEnumStmt:
 			retval = _equalCreateEnumStmt(a, b);
 			break;
+		case T_CreateRangeStmt:
+			retval = _equalCreateRangeStmt(a, b);
+			break;
 		case T_AlterEnumStmt:
 			retval = _equalAlterEnumStmt(a, b);
 			break;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index f0a4b0efa4a3137e596b0f6b0effa4a0e4347444..c135465fbd38cc67301ee137b083f8c5ea8baf14 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4336,6 +4336,13 @@ DefineStmt:
 					n->vals = $7;
 					$$ = (Node *)n;
 				}
+			| CREATE TYPE_P any_name AS RANGE definition
+				{
+					CreateRangeStmt *n = makeNode(CreateRangeStmt);
+					n->typeName = $3;
+					n->params	= $6;
+					$$ = (Node *)n;
+				}
 			| CREATE TEXT_P SEARCH PARSER any_name definition
 				{
 					DefineStmt *n = makeNode(DefineStmt);
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 127818abdeaf04a2a5449fd5cc56178e07d4cb93..a461ac9aef1c60e10a0081e33ca4814a951f35a0 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -158,7 +158,8 @@ coerce_type(ParseState *pstate, Node *node,
 		return node;
 	}
 	if (targetTypeId == ANYARRAYOID ||
-		targetTypeId == ANYENUMOID)
+		targetTypeId == ANYENUMOID ||
+		targetTypeId == ANYRANGEOID)
 	{
 		/*
 		 * Assume can_coerce_type verified that implicit coercion is okay.
@@ -1275,9 +1276,11 @@ coerce_to_common_type(ParseState *pstate, Node *node,
  * 1) All arguments declared ANYARRAY must have matching datatypes,
  *	  and must in fact be varlena arrays.
  * 2) All arguments declared ANYELEMENT must have matching datatypes.
- * 3) If there are arguments of both ANYELEMENT and ANYARRAY, make sure
- *	  the actual ANYELEMENT datatype is in fact the element type for
- *	  the actual ANYARRAY datatype.
+ * 3) If there are arguments of both ANYELEMENT and ANYARRAY, make sure the
+ *	  actual ANYELEMENT datatype is in fact the element type for the actual
+ *	  ANYARRAY datatype. Similarly, if there are arguments of both ANYELEMENT
+ *	  and ANYRANGE, make sure the actual ANYELEMENT datatype is in fact the
+ *	  subtype for the actual ANYRANGE type.
  * 4) ANYENUM is treated the same as ANYELEMENT except that if it is used
  *	  (alone or in combination with plain ANYELEMENT), we add the extra
  *	  condition that the ANYELEMENT type must be an enum.
@@ -1311,6 +1314,8 @@ check_generic_type_consistency(Oid *actual_arg_types,
 	Oid			elem_typeid = InvalidOid;
 	Oid			array_typeid = InvalidOid;
 	Oid			array_typelem;
+	Oid			range_typeid = InvalidOid;
+	Oid			range_typelem;
 	bool		have_anyelement = false;
 	bool		have_anynonarray = false;
 	bool		have_anyenum = false;
@@ -1348,6 +1353,15 @@ check_generic_type_consistency(Oid *actual_arg_types,
 				return false;
 			array_typeid = actual_type;
 		}
+		else if (decl_type == ANYRANGEOID)
+		{
+			if (actual_type == UNKNOWNOID)
+				continue;
+			actual_type = getBaseType(actual_type);
+			if (OidIsValid(range_typeid) && actual_type != range_typeid)
+				return false;
+			range_typeid = actual_type;
+		}
 	}
 
 	/* Get the element type based on the array type, if we have one */
@@ -1393,6 +1407,27 @@ check_generic_type_consistency(Oid *actual_arg_types,
 			return false;
 	}
 
+	/* Get the element type based on the range type, if we have one */
+	if (OidIsValid(range_typeid))
+	{
+		range_typelem = get_range_subtype(range_typeid);
+		if (!OidIsValid(range_typelem))
+			return false;		/* should be a range, but isn't */
+
+		if (!OidIsValid(elem_typeid))
+		{
+			/*
+			 * if we don't have an element type yet, use the one we just got
+			 */
+			elem_typeid = range_typelem;
+		}
+		else if (range_typelem != elem_typeid)
+		{
+			/* otherwise, they better match */
+			return false;
+		}
+	}
+
 	/* Looks valid */
 	return true;
 }
@@ -1416,23 +1451,28 @@ check_generic_type_consistency(Oid *actual_arg_types,
  * if it is declared as a polymorphic type:
  *
  * 1) If return type is ANYARRAY, and any argument is ANYARRAY, use the
- *	  argument's actual type as the function's return type.
- * 2) If return type is ANYARRAY, no argument is ANYARRAY, but any argument
- *	  is ANYELEMENT, use the actual type of the argument to determine
- *	  the function's return type, i.e. the element type's corresponding
- *	  array type.
+ *	  argument's actual type as the function's return type. Similarly, if
+ *	  return type is ANYRANGE, and any argument is ANYRANGE, use the argument's
+ *	  actual type as the function's return type.
+ * 2) If return type is ANYARRAY, no argument is ANYARRAY, but any argument is
+ *	  ANYELEMENT, use the actual type of the argument to determine the
+ *	  function's return type, i.e. the element type's corresponding array
+ *	  type. Note: similar behavior does not exist for ANYRANGE, because it's
+ *	  impossble to determine the range type from the subtype alone.\
  * 3) If return type is ANYARRAY, no argument is ANYARRAY or ANYELEMENT,
- *	  generate an ERROR. This condition is prevented by CREATE FUNCTION
- *	  and is therefore not expected here.
+ *	  generate an ERROR. Similarly, if the return type is ANYRANGE, and no
+ *	  argument is ANYRANGE or ANYELEMENT, generate an error. These conditions
+ *	  are prevented by CREATE FUNCTION and is therefore not expected here.
  * 4) If return type is ANYELEMENT, and any argument is ANYELEMENT, use the
  *	  argument's actual type as the function's return type.
- * 5) If return type is ANYELEMENT, no argument is ANYELEMENT, but any
- *	  argument is ANYARRAY, use the actual type of the argument to determine
- *	  the function's return type, i.e. the array type's corresponding
- *	  element type.
- * 6) If return type is ANYELEMENT, no argument is ANYARRAY or ANYELEMENT,
- *	  generate an ERROR. This condition is prevented by CREATE FUNCTION
- *	  and is therefore not expected here.
+ * 5) If return type is ANYELEMENT, no argument is ANYELEMENT, but any argument
+ *	  is ANYARRAY or ANYRANGE, use the actual type of the argument to determine
+ *	  the function's return type; i.e. the array type's corresponding element
+ *	  type or the range type's corresponding subtype (or both, in which case
+ *	  they must match).
+ * 6) If return type is ANYELEMENT, no argument is ANYARRAY, ANYRANGE, or
+ *	  ANYELEMENT, generate an ERROR. This condition is prevented by CREATE
+ *	  FUNCTION and is therefore not expected here.
  * 7) ANYENUM is treated the same as ANYELEMENT except that if it is used
  *	  (alone or in combination with plain ANYELEMENT), we add the extra
  *	  condition that the ANYELEMENT type must be an enum.
@@ -1441,9 +1481,10 @@ check_generic_type_consistency(Oid *actual_arg_types,
  *	  (This is a no-op if used in combination with ANYARRAY or ANYENUM, but
  *	  is an extra restriction if not.)
  *
- * Domains over arrays match ANYARRAY arguments, and are immediately flattened
- * to their base type.	(In particular, if the return type is also ANYARRAY,
- * we'll set it to the base type not the domain type.)
+ * Domains over arrays or ranges match ANYARRAY or ANYRANGE arguments,
+ * respectively, and are immediately flattened to their base type. (In
+ * particular, if the return type is also ANYARRAY or ANYRANGE, we'll set it to
+ * the base type not the domain type.)
  *
  * When allow_poly is false, we are not expecting any of the actual_arg_types
  * to be polymorphic, and we should not return a polymorphic result type
@@ -1473,7 +1514,9 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
 	bool		have_unknowns = false;
 	Oid			elem_typeid = InvalidOid;
 	Oid			array_typeid = InvalidOid;
+	Oid			range_typeid = InvalidOid;
 	Oid			array_typelem;
+	Oid			range_typelem;
 	bool		have_anyelement = (rettype == ANYELEMENTOID ||
 								   rettype == ANYNONARRAYOID ||
 								   rettype == ANYENUMOID);
@@ -1534,6 +1577,26 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
 								   format_type_be(actual_type))));
 			array_typeid = actual_type;
 		}
+		else if (decl_type == ANYRANGEOID)
+		{
+			have_generics = true;
+			if (actual_type == UNKNOWNOID)
+			{
+				have_unknowns = true;
+				continue;
+			}
+			if (allow_poly && decl_type == actual_type)
+				continue;		/* no new information here */
+			actual_type = getBaseType(actual_type);		/* flatten domains */
+			if (OidIsValid(range_typeid) && actual_type != range_typeid)
+				ereport(ERROR,
+						(errcode(ERRCODE_DATATYPE_MISMATCH),
+				 errmsg("arguments declared \"anyrange\" are not all alike"),
+						 errdetail("%s versus %s",
+								   format_type_be(range_typeid),
+								   format_type_be(actual_type))));
+			range_typeid = actual_type;
+		}
 	}
 
 	/*
@@ -1579,6 +1642,34 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
 							   format_type_be(elem_typeid))));
 		}
 	}
+	/* Get the element type based on the range type, if we have one */
+	else if (OidIsValid(range_typeid))
+	{
+		range_typelem = get_range_subtype(range_typeid);
+		if (!OidIsValid(range_typelem))
+			ereport(ERROR,
+					(errcode(ERRCODE_DATATYPE_MISMATCH),
+					 errmsg("argument declared \"anyrange\" is not a range but type %s",
+							format_type_be(range_typeid))));
+
+		if (!OidIsValid(elem_typeid))
+		{
+			/*
+			 * if we don't have an element type yet, use the one we just got
+			 */
+			elem_typeid = range_typelem;
+		}
+		else if (range_typelem != elem_typeid)
+		{
+			/* otherwise, they better match */
+			ereport(ERROR,
+					(errcode(ERRCODE_DATATYPE_MISMATCH),
+					 errmsg("argument declared \"anyrange\" is not consistent with argument declared \"anyelement\""),
+					 errdetail("%s versus %s",
+							   format_type_be(range_typeid),
+							   format_type_be(elem_typeid))));
+		}
+	}
 	else if (!OidIsValid(elem_typeid))
 	{
 		if (allow_poly)
@@ -1645,6 +1736,17 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
 				}
 				declared_arg_types[j] = array_typeid;
 			}
+			else if (decl_type == ANYRANGEOID)
+			{
+				if (!OidIsValid(range_typeid))
+				{
+					ereport(ERROR,
+							(errcode(ERRCODE_UNDEFINED_OBJECT),
+						 errmsg("could not find range type for data type %s",
+								format_type_be(elem_typeid))));
+				}
+				declared_arg_types[j] = range_typeid;
+			}
 		}
 	}
 
@@ -1663,6 +1765,19 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
 		return array_typeid;
 	}
 
+	/* if we return ANYRANGE use the appropriate argument type */
+	if (rettype == ANYRANGEOID)
+	{
+		if (!OidIsValid(range_typeid))
+		{
+			ereport(ERROR,
+					(errcode(ERRCODE_UNDEFINED_OBJECT),
+					 errmsg("could not find range type for data type %s",
+							format_type_be(elem_typeid))));
+		}
+		return range_typeid;
+	}
+
 	/* if we return ANYELEMENT use the appropriate argument type */
 	if (rettype == ANYELEMENTOID ||
 		rettype == ANYNONARRAYOID ||
@@ -1711,7 +1826,8 @@ resolve_generic_type(Oid declared_type,
 		}
 		else if (context_declared_type == ANYELEMENTOID ||
 				 context_declared_type == ANYNONARRAYOID ||
-				 context_declared_type == ANYENUMOID)
+				 context_declared_type == ANYENUMOID ||
+				 context_declared_type == ANYRANGEOID)
 		{
 			/* Use the array type corresponding to actual type */
 			Oid			array_typeid = get_array_type(context_actual_type);
@@ -1726,7 +1842,8 @@ resolve_generic_type(Oid declared_type,
 	}
 	else if (declared_type == ANYELEMENTOID ||
 			 declared_type == ANYNONARRAYOID ||
-			 declared_type == ANYENUMOID)
+			 declared_type == ANYENUMOID ||
+			 declared_type == ANYRANGEOID)
 	{
 		if (context_declared_type == ANYARRAYOID)
 		{
@@ -1741,6 +1858,18 @@ resolve_generic_type(Oid declared_type,
 								format_type_be(context_base_type))));
 			return array_typelem;
 		}
+		else if (context_declared_type == ANYRANGEOID)
+		{
+			/* Use the element type corresponding to actual type */
+			Oid			range_typelem = get_range_subtype(context_actual_type);
+
+			if (!OidIsValid(range_typelem))
+				ereport(ERROR,
+						(errcode(ERRCODE_DATATYPE_MISMATCH),
+						 errmsg("argument declared \"anyrange\" is not a range but type %s",
+								format_type_be(context_actual_type))));
+			return range_typelem;
+		}
 		else if (context_declared_type == ANYELEMENTOID ||
 				 context_declared_type == ANYNONARRAYOID ||
 				 context_declared_type == ANYENUMOID)
@@ -1854,6 +1983,11 @@ IsBinaryCoercible(Oid srctype, Oid targettype)
 		if (type_is_enum(srctype))
 			return true;
 
+	/* Also accept any range type as coercible to ANYRANGE */
+	if (targettype == ANYRANGEOID)
+		if (type_is_range(srctype))
+			return true;
+
 	/* Also accept any composite type as coercible to RECORD */
 	if (targettype == RECORDOID)
 		if (ISCOMPLEX(srctype))
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index a74019a0d65a76445952833acc21d194378b346a..5b0633398cf631e3cd33bce1ae5432d76d04e8ee 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -202,6 +202,7 @@ check_xact_readonly(Node *parsetree)
 		case T_CreateTrigStmt:
 		case T_CompositeTypeStmt:
 		case T_CreateEnumStmt:
+		case T_CreateRangeStmt:
 		case T_AlterEnumStmt:
 		case T_ViewStmt:
 		case T_DropCastStmt:
@@ -870,6 +871,10 @@ standard_ProcessUtility(Node *parsetree,
 			DefineEnum((CreateEnumStmt *) parsetree);
 			break;
 
+		case T_CreateRangeStmt:
+			DefineRange((CreateRangeStmt *) parsetree);
+			break;
+
 		case T_AlterEnumStmt:	/* ALTER TYPE (enum) */
 
 			/*
@@ -1854,6 +1859,10 @@ CreateCommandTag(Node *parsetree)
 			tag = "CREATE TYPE";
 			break;
 
+		case T_CreateRangeStmt:
+			tag = "CREATE TYPE";
+			break;
+
 		case T_AlterEnumStmt:
 			tag = "ALTER TYPE";
 			break;
@@ -2401,6 +2410,10 @@ GetCommandLogLevel(Node *parsetree)
 			lev = LOGSTMT_DDL;
 			break;
 
+		case T_CreateRangeStmt:
+			lev = LOGSTMT_DDL;
+			break;
+
 		case T_AlterEnumStmt:
 			lev = LOGSTMT_DDL;
 			break;
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index ce28abd9fe1e5d725c52a236c507f5fd731552d3..5f968b011fcd637631eaceca082fd75d90c9360d 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -20,8 +20,8 @@ OBJS = acl.o arrayfuncs.o array_userfuncs.o arrayutils.o bool.o \
 	enum.o float.o format_type.o \
 	geo_ops.o geo_selfuncs.o int.o int8.o like.o lockfuncs.o \
 	misc.o nabstime.o name.o numeric.o numutils.o \
-	oid.o oracle_compat.o pseudotypes.o rowtypes.o \
-	regexp.o regproc.o ruleutils.o selfuncs.o \
+	oid.o oracle_compat.o pseudotypes.o rangetypes.o rangetypes_gist.o \
+	rowtypes.o regexp.o regproc.o ruleutils.o selfuncs.o \
 	tid.o timestamp.o varbit.o varchar.o varlena.o version.o xid.o \
 	network.o mac.o inet_cidr_ntop.o inet_net_pton.o \
 	ri_triggers.o pg_lzcompress.o pg_locale.o formatting.o \
diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c
index b06faf0c720e0afd152eb705f930da3cd96b2c25..271b57a83027c7f11e5d2f771e183829ffd35a94 100644
--- a/src/backend/utils/adt/date.c
+++ b/src/backend/utils/adt/date.c
@@ -889,7 +889,6 @@ date_timestamp(PG_FUNCTION_ARGS)
 	PG_RETURN_TIMESTAMP(result);
 }
 
-
 /* timestamp_date()
  * Convert timestamp to date data type.
  */
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index ddb1bd2b71ccf577b7cec15942bd257e5ad83e23..3b78c36a7ec6f0de98b9587ae0e2c374bf19d3ab 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -25,6 +25,7 @@
 #include "libpq/pqformat.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
+#include "utils/rangetypes.h"
 
 
 /*
@@ -187,6 +188,29 @@ anyenum_out(PG_FUNCTION_ARGS)
 	return enum_out(fcinfo);
 }
 
+/*
+ * anyrange_in		- input routine for pseudo-type ANYRANGE.
+ */
+Datum
+anyrange_in(PG_FUNCTION_ARGS)
+{
+	ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			 errmsg("cannot accept a value of type anyrange")));
+
+	PG_RETURN_VOID();			/* keep compiler quiet */
+}
+
+/*
+ * anyrange_out		- output routine for pseudo-type ANYRANGE.
+ *
+ * We may as well allow this, since range_out will in fact work.
+ */
+Datum
+anyrange_out(PG_FUNCTION_ARGS)
+{
+	return range_out(fcinfo);
+}
 
 /*
  * void_in		- input routine for pseudo-type VOID.
diff --git a/src/backend/utils/adt/rangetypes.c b/src/backend/utils/adt/rangetypes.c
new file mode 100644
index 0000000000000000000000000000000000000000..6327d33f3ae0001f5cc878bdf82fea6422d24f10
--- /dev/null
+++ b/src/backend/utils/adt/rangetypes.c
@@ -0,0 +1,2153 @@
+/*-------------------------------------------------------------------------
+ *
+ * rangetypes.c
+ *	  I/O functions, operators, and support functions for range types
+ *
+ * Copyright (c) 2006-2011, PostgreSQL Global Development Group
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/utils/adt/rangetypes.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/hash.h"
+#include "access/nbtree.h"
+#include "catalog/pg_opclass.h"
+#include "catalog/pg_range.h"
+#include "catalog/pg_type.h"
+#include "fmgr.h"
+#include "lib/stringinfo.h"
+#include "libpq/pqformat.h"
+#include "utils/builtins.h"
+#include "utils/date.h"
+#include "utils/fmgroids.h"
+#include "utils/int8.h"
+#include "utils/lsyscache.h"
+#include "utils/rangetypes.h"
+#include "utils/syscache.h"
+#include "utils/timestamp.h"
+#include "utils/typcache.h"
+
+#define TYPE_IS_PACKABLE(typlen, typstorage) \
+	(typlen == -1 && typstorage != 'p')
+
+/* flags */
+#define RANGE_EMPTY		0x01
+#define RANGE_LB_INC	0x02
+#define RANGE_LB_NULL	0x04 /* NOT USED */
+#define RANGE_LB_INF	0x08
+#define RANGE_UB_INC	0x10
+#define RANGE_UB_NULL	0x20 /* NOT USED */
+#define RANGE_UB_INF	0x40
+
+#define RANGE_HAS_LBOUND(flags) (!(flags & (RANGE_EMPTY |   \
+											RANGE_LB_NULL |	\
+											RANGE_LB_INF)))
+
+#define RANGE_HAS_UBOUND(flags) (!(flags & (RANGE_EMPTY |	\
+											RANGE_UB_NULL |	\
+											RANGE_UB_INF)))
+
+#define RANGE_EMPTY_LITERAL "empty"
+
+static void range_parse(char *input_str,  char *flags, char **lbound_str,
+						char **ubound_str);
+static char *range_parse_bound(char *string, char *ptr, char **bound_str,
+							   bool *infinite);
+static char *range_deparse(char flags, char *lbound_str, char *ubound_str);
+static char *range_bound_escape(char *in_str);
+static bool range_contains_internal(FunctionCallInfo fcinfo, RangeType *r1,
+									RangeType *r2);
+static Size datum_compute_size(Size sz, Datum datum, bool typbyval,
+							   char typalign, int16 typlen, char typstorage);
+static Pointer datum_write(Pointer ptr, Datum datum, bool typbyval,
+						   char typalign, int16 typlen, char typstorage);
+
+/*
+ *----------------------------------------------------------
+ * I/O FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+Datum
+range_in(PG_FUNCTION_ARGS)
+{
+	char		*input_str = PG_GETARG_CSTRING(0);
+	Oid			 rngtypoid = PG_GETARG_OID(1);
+	Oid			 typmod	   = PG_GETARG_INT32(2);
+
+	char		 flags;
+	Datum		 range;
+	char		*lbound_str;
+	char		*ubound_str;
+
+	regproc		subInput;
+	FmgrInfo	subInputFn;
+	Oid			ioParam;
+
+	RangeTypeInfo		rngtypinfo;
+	RangeBound			lower;
+	RangeBound			upper;
+
+	if (rngtypoid == ANYRANGEOID)
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("cannot accept a value of type anyrange")));
+
+	range_gettypinfo(fcinfo, rngtypoid, &rngtypinfo);
+
+	/* parse */
+	range_parse(input_str, &flags, &lbound_str, &ubound_str);
+
+	/* input */
+	getTypeInputInfo(rngtypinfo.subtype, &subInput, &ioParam);
+	fmgr_info(subInput, &subInputFn);
+
+	lower.rngtypid	= rngtypoid;
+	lower.infinite	= (flags & RANGE_LB_INF) != 0;
+	lower.inclusive = (flags & RANGE_LB_INC) != 0;
+	lower.lower		= true;
+	upper.rngtypid	= rngtypoid;
+	upper.infinite	= (flags & RANGE_UB_INF) != 0;
+	upper.inclusive = (flags & RANGE_UB_INC) != 0;
+	upper.lower		= false;
+
+	if (RANGE_HAS_LBOUND(flags))
+		lower.val = InputFunctionCall(&subInputFn, lbound_str,
+									   ioParam, typmod);
+	if (RANGE_HAS_UBOUND(flags))
+		upper.val = InputFunctionCall(&subInputFn, ubound_str,
+									   ioParam, typmod);
+
+	/* serialize and canonicalize */
+	range = make_range(fcinfo, &lower, &upper, flags & RANGE_EMPTY);
+
+	PG_RETURN_RANGE(range);
+}
+
+Datum
+range_out(PG_FUNCTION_ARGS)
+{
+	RangeType  *range = PG_GETARG_RANGE(0);
+
+	regproc		subOutput;
+	FmgrInfo	subOutputFn;
+	bool		isVarlena;
+
+	char		flags = 0;
+	char	   *lbound_str = NULL;
+	char	   *ubound_str = NULL;
+	char	   *output_str;
+
+	bool		empty;
+
+	RangeTypeInfo		rngtypinfo;
+	RangeBound			lower;
+	RangeBound			upper;
+
+	/* deserialize */
+	range_deserialize(fcinfo, range, &lower, &upper, &empty);
+
+	if (lower.rngtypid != upper.rngtypid)
+		elog(ERROR, "range types do not match");
+
+	range_gettypinfo(fcinfo, lower.rngtypid, &rngtypinfo);
+
+	if (empty)
+		flags |= RANGE_EMPTY;
+
+	flags |= (lower.inclusive)	? RANGE_LB_INC  : 0;
+	flags |= (lower.infinite)	? RANGE_LB_INF  : 0;
+	flags |= (upper.inclusive)	? RANGE_UB_INC  : 0;
+	flags |= (upper.infinite)	? RANGE_UB_INF  : 0;
+
+	/* output */
+	getTypeOutputInfo(rngtypinfo.subtype, &subOutput, &isVarlena);
+	fmgr_info(subOutput, &subOutputFn);
+
+	if (RANGE_HAS_LBOUND(flags))
+		lbound_str = OutputFunctionCall(&subOutputFn, lower.val);
+	if (RANGE_HAS_UBOUND(flags))
+		ubound_str = OutputFunctionCall(&subOutputFn, upper.val);
+
+	/* deparse */
+	output_str = range_deparse(flags, lbound_str, ubound_str);
+
+	PG_RETURN_CSTRING(output_str);
+}
+
+/*
+ * Binary representation: The first byte is the flags, then 4 bytes are the
+ * range type Oid, then the lower bound (if present) then the upper bound (if
+ * present). Each bound is represented by a 4-byte length header and the binary
+ * representation of that bound (as returned by a call to the send function for
+ * the subtype).
+ */
+
+Datum
+range_recv(PG_FUNCTION_ARGS)
+{
+	StringInfo	buf		 = (StringInfo) PG_GETARG_POINTER(0);
+	Oid			rngtypid = PG_GETARG_OID(1);
+	int32		typmod	 = PG_GETARG_INT32(2);
+	Oid			subrecv;
+	Oid			ioparam;
+	Datum		range;
+	char		flags;
+	RangeBound	lower;
+	RangeBound	upper;
+
+	RangeTypeInfo rngtypinfo;
+
+	flags = (unsigned char) pq_getmsgbyte(buf);
+
+	range_gettypinfo(fcinfo, rngtypid, &rngtypinfo);
+
+	getTypeBinaryInputInfo(rngtypinfo.subtype, &subrecv, &ioparam);
+
+	if (RANGE_HAS_LBOUND(flags))
+	{
+		uint32			 bound_len	= pq_getmsgint(buf, 4);
+		const char		*bound_data = pq_getmsgbytes(buf, bound_len);
+		StringInfoData	 bound_buf;
+
+		initStringInfo(&bound_buf);
+		appendBinaryStringInfo(&bound_buf, bound_data, bound_len);
+
+		lower.val = OidReceiveFunctionCall(subrecv,
+										   &bound_buf,
+										   ioparam,
+										   typmod);
+		pfree(bound_buf.data);
+	}
+	else
+		lower.val = (Datum) 0;
+
+	if (RANGE_HAS_UBOUND(flags))
+	{
+		uint32			 bound_len	= pq_getmsgint(buf, 4);
+		const char		*bound_data = pq_getmsgbytes(buf, bound_len);
+		StringInfoData	 bound_buf;
+
+		initStringInfo(&bound_buf);
+		appendBinaryStringInfo(&bound_buf, bound_data, bound_len);
+
+		upper.val = OidReceiveFunctionCall(subrecv,
+										   &bound_buf,
+										   ioparam,
+										   typmod);
+		pfree(bound_buf.data);
+	}
+	else
+		upper.val = (Datum) 0;
+
+	pq_getmsgend(buf);
+
+	lower.rngtypid	= rngtypid;
+	lower.infinite	= (flags & RANGE_LB_INF) != 0;
+	lower.inclusive = (flags & RANGE_LB_INC) != 0;
+	lower.lower		= true;
+	upper.rngtypid	= rngtypid;
+	upper.infinite	= (flags & RANGE_UB_INF) != 0;
+	upper.inclusive = (flags & RANGE_UB_INC) != 0;
+	upper.lower		= false;
+
+	/* serialize and canonicalize */
+	range = make_range(fcinfo, &lower, &upper, flags & RANGE_EMPTY);
+
+	/*
+	 * XXX if the subtype is pass-by-val, we should pfree the upper and
+	 * lower bounds here.
+	 */
+
+	PG_RETURN_RANGE(range);
+}
+
+Datum
+range_send(PG_FUNCTION_ARGS)
+{
+	RangeType	*range = PG_GETARG_RANGE(0);
+	StringInfo	 buf   = makeStringInfo();
+	char		 flags = 0;
+	RangeBound	 lower;
+	RangeBound	 upper;
+	bool		 empty;
+	Oid			 subsend;
+	bool		 typIsVarlena;
+
+	RangeTypeInfo rngtypinfo;
+
+	pq_begintypsend(buf);
+
+	range_deserialize(fcinfo, range, &lower, &upper, &empty);
+
+	if (empty)
+		flags |= RANGE_EMPTY;
+
+	flags |= (lower.inclusive)	? RANGE_LB_INC  : 0;
+	flags |= (lower.infinite)	? RANGE_LB_INF  : 0;
+	flags |= (upper.inclusive)	? RANGE_UB_INC  : 0;
+	flags |= (upper.infinite)	? RANGE_UB_INF  : 0;
+
+	range_gettypinfo(fcinfo, lower.rngtypid, &rngtypinfo);
+
+	getTypeBinaryOutputInfo(rngtypinfo.subtype,
+							&subsend, &typIsVarlena);
+
+	pq_sendbyte(buf, flags);
+
+	if (RANGE_HAS_LBOUND(flags))
+	{
+		Datum	 bound		= PointerGetDatum(
+			OidSendFunctionCall(subsend, lower.val));
+		uint32	 bound_len	= VARSIZE(bound) - VARHDRSZ;
+		char	*bound_data = VARDATA(bound);
+
+		pq_sendint(buf, bound_len, 4);
+		pq_sendbytes(buf, bound_data, bound_len);
+	}
+
+	if (RANGE_HAS_UBOUND(flags))
+	{
+		Datum	 bound		= PointerGetDatum(
+			OidSendFunctionCall(subsend, upper.val));
+		uint32	 bound_len	= VARSIZE(bound) - VARHDRSZ;
+		char	*bound_data = VARDATA(bound);
+
+		pq_sendint(buf, bound_len, 4);
+		pq_sendbytes(buf, bound_data, bound_len);
+	}
+
+	PG_RETURN_BYTEA_P(pq_endtypsend(buf));
+}
+
+
+/*
+ *----------------------------------------------------------
+ * GENERIC FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+Datum
+range_constructor0(PG_FUNCTION_ARGS)
+{
+	Oid			 rngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+	RangeType	*range;
+	RangeBound	 lower;
+	RangeBound	 upper;
+
+	lower.rngtypid	= rngtypid;
+	lower.val		= (Datum) 0;
+	lower.inclusive = false;
+	lower.infinite	= false;
+	lower.lower		= true;
+
+	upper.rngtypid	= rngtypid;
+	upper.val		= (Datum) 0;
+	upper.inclusive = false;
+	upper.infinite	= false;
+	upper.lower		= false;
+
+	range = DatumGetRangeType(make_range(fcinfo, &lower, &upper, true));
+
+	PG_RETURN_RANGE(range);
+}
+
+Datum
+range_constructor1(PG_FUNCTION_ARGS)
+{
+	Datum		 arg1	  = PG_GETARG_DATUM(0);
+	Oid			 rngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+	RangeType	*range;
+	RangeBound	 lower;
+	RangeBound	 upper;
+
+	if (PG_ARGISNULL(0))
+		ereport(ERROR,
+				(errcode(ERRCODE_DATA_EXCEPTION),
+				 errmsg("argument must not be NULL")));
+
+	lower.rngtypid	= rngtypid;
+	lower.val		= arg1;
+	lower.inclusive = true;
+	lower.infinite	= false;
+	lower.lower		= true;
+
+	upper.rngtypid	= rngtypid;
+	upper.val		= arg1;
+	upper.inclusive = true;
+	upper.infinite	= false;
+	upper.lower		= false;
+
+	range = DatumGetRangeType(make_range(fcinfo, &lower, &upper, false));
+
+	PG_RETURN_RANGE(range);
+}
+
+Datum
+range_constructor2(PG_FUNCTION_ARGS)
+{
+	Datum		 arg1	  = PG_GETARG_DATUM(0);
+	Datum		 arg2	  = PG_GETARG_DATUM(1);
+	Oid			 rngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+	RangeType	*range;
+	RangeBound	 lower;
+	RangeBound	 upper;
+	char		 flags;
+
+	flags = range_parse_flags(RANGE_DEFAULT_FLAGS);
+
+	lower.rngtypid	= rngtypid;
+	lower.val		= PG_ARGISNULL(0) ? (Datum)0 : arg1;
+	lower.inclusive = flags & RANGE_LB_INC;
+	lower.infinite	= PG_ARGISNULL(0);
+	lower.lower		= true;
+
+	upper.rngtypid	= rngtypid;
+	upper.val		= PG_ARGISNULL(1) ? (Datum)0 : arg2;
+	upper.inclusive = flags & RANGE_UB_INC;
+	upper.infinite	= PG_ARGISNULL(1);
+	upper.lower		= false;
+
+	range = DatumGetRangeType(make_range(fcinfo, &lower, &upper, false));
+
+	PG_RETURN_RANGE(range);
+}
+
+Datum
+range_constructor3(PG_FUNCTION_ARGS)
+{
+	Datum		 arg1	  = PG_GETARG_DATUM(0);
+	Datum		 arg2	  = PG_GETARG_DATUM(1);
+	Oid			 rngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+	RangeType	*range;
+	RangeBound	 lower;
+	RangeBound	 upper;
+	char		 flags;
+
+	if (PG_ARGISNULL(2))
+		ereport(ERROR,
+				(errcode(ERRCODE_DATA_EXCEPTION),
+				 errmsg("flags argument must not be NULL")));
+	flags = range_parse_flags(text_to_cstring(PG_GETARG_TEXT_P(2)));
+
+	lower.rngtypid	= rngtypid;
+	lower.val		= PG_ARGISNULL(0) ? (Datum)0 : arg1;
+	lower.inclusive = flags & RANGE_LB_INC;
+	lower.infinite	= PG_ARGISNULL(0);
+	lower.lower		= true;
+
+	upper.rngtypid	= rngtypid;
+	upper.val		= PG_ARGISNULL(1) ? (Datum)0 : arg2;
+	upper.inclusive = flags & RANGE_UB_INC;
+	upper.infinite	= PG_ARGISNULL(1);
+	upper.lower		= false;
+
+	range = DatumGetRangeType(make_range(fcinfo, &lower, &upper, false));
+
+	PG_RETURN_RANGE(range);
+}
+
+/* range -> subtype */
+Datum
+range_lower(PG_FUNCTION_ARGS)
+{
+	RangeType	*r1 = PG_GETARG_RANGE(0);
+	RangeBound	 lower;
+	RangeBound	 upper;
+	bool		 empty;
+
+	range_deserialize(fcinfo, r1, &lower, &upper, &empty);
+
+	if (empty)
+		ereport(ERROR,
+				(errcode(ERRCODE_DATA_EXCEPTION),
+				 errmsg("empty range has no lower bound")));
+	if (lower.infinite)
+		ereport(ERROR,
+				(errcode(ERRCODE_DATA_EXCEPTION),
+				 errmsg("range lower bound is infinite")));
+
+	PG_RETURN_DATUM(lower.val);
+}
+
+Datum
+range_upper(PG_FUNCTION_ARGS)
+{
+	RangeType	*r1 = PG_GETARG_RANGE(0);
+	RangeBound	 lower;
+	RangeBound	 upper;
+	bool		 empty;
+
+	range_deserialize(fcinfo, r1, &lower, &upper, &empty);
+
+	if (empty)
+		ereport(ERROR,
+				(errcode(ERRCODE_DATA_EXCEPTION),
+				 errmsg("empty range has no upper bound")));
+	if (upper.infinite)
+		ereport(ERROR,
+				(errcode(ERRCODE_DATA_EXCEPTION),
+				 errmsg("range upper bound is infinite")));
+
+	PG_RETURN_DATUM(upper.val);
+}
+
+
+/* range -> bool */
+Datum
+range_empty(PG_FUNCTION_ARGS)
+{
+	RangeType	*r1 = PG_GETARG_RANGE(0);
+	RangeBound	 lower;
+	RangeBound	 upper;
+	bool		 empty;
+
+	range_deserialize(fcinfo, r1, &lower, &upper, &empty);
+
+	PG_RETURN_BOOL(empty);
+}
+
+Datum
+range_lower_inc(PG_FUNCTION_ARGS)
+{
+	RangeType	*r1 = PG_GETARG_RANGE(0);
+	RangeBound	 lower;
+	RangeBound	 upper;
+	bool		 empty;
+
+	range_deserialize(fcinfo, r1, &lower, &upper, &empty);
+
+	PG_RETURN_BOOL(lower.inclusive);
+}
+
+Datum
+range_upper_inc(PG_FUNCTION_ARGS)
+{
+	RangeType	*r1 = PG_GETARG_RANGE(0);
+	RangeBound	 lower;
+	RangeBound	 upper;
+	bool		 empty;
+
+	range_deserialize(fcinfo, r1, &lower, &upper, &empty);
+
+	PG_RETURN_BOOL(upper.inclusive);
+}
+
+Datum
+range_lower_inf(PG_FUNCTION_ARGS)
+{
+	RangeType	*r1 = PG_GETARG_RANGE(0);
+	RangeBound	 lower;
+	RangeBound	 upper;
+	bool		 empty;
+
+	range_deserialize(fcinfo, r1, &lower, &upper, &empty);
+
+	PG_RETURN_BOOL(lower.infinite);
+}
+
+Datum
+range_upper_inf(PG_FUNCTION_ARGS)
+{
+	RangeType	*r1 = PG_GETARG_RANGE(0);
+	RangeBound	 lower;
+	RangeBound	 upper;
+	bool		 empty;
+
+	range_deserialize(fcinfo, r1, &lower, &upper, &empty);
+
+	PG_RETURN_BOOL(upper.infinite);
+}
+
+
+/* range, range -> bool */
+Datum
+range_eq(PG_FUNCTION_ARGS)
+{
+	RangeType *r1 = PG_GETARG_RANGE(0);
+	RangeType *r2 = PG_GETARG_RANGE(1);
+
+	RangeBound	lower1, lower2;
+	RangeBound	upper1, upper2;
+	bool		empty1, empty2;
+
+	range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
+	range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
+
+	if (lower1.rngtypid != upper1.rngtypid ||
+		lower1.rngtypid != lower2.rngtypid ||
+		lower1.rngtypid != upper2.rngtypid)
+		elog(ERROR, "range types do not match");
+
+	if (empty1 && empty2)
+		PG_RETURN_BOOL(true);
+	if (empty1 != empty2)
+		PG_RETURN_BOOL(false);
+
+	if (range_cmp_bounds(fcinfo, &lower1, &lower2) != 0)
+		PG_RETURN_BOOL(false);
+
+	if (range_cmp_bounds(fcinfo, &upper1, &upper2) != 0)
+		PG_RETURN_BOOL(false);
+
+	PG_RETURN_BOOL(true);
+}
+
+Datum
+range_ne(PG_FUNCTION_ARGS)
+{
+	bool eq = DatumGetBool(range_eq(fcinfo));
+
+	PG_RETURN_BOOL(!eq);
+}
+
+Datum
+range_contains_elem(PG_FUNCTION_ARGS)
+{
+	RangeType	*r1		  = PG_GETARG_RANGE(0);
+	RangeType	*r2;
+	Datum		 val	  = PG_GETARG_DATUM(1);
+
+	RangeBound	lower1, lower2;
+	RangeBound	upper1, upper2;
+	bool		empty1;
+
+	range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
+
+	lower2.rngtypid	 = lower1.rngtypid;
+	lower2.inclusive = true;
+	lower2.infinite	 = false;
+	lower2.lower	 = true;
+	lower2.val		 = val;
+
+	upper2.rngtypid	 = lower1.rngtypid;
+	upper2.inclusive = true;
+	upper2.infinite	 = false;
+	upper2.lower	 = false;
+	upper2.val		 = val;
+
+	r2 = DatumGetRangeType(make_range(fcinfo, &lower2, &upper2, false));
+
+	PG_RETURN_BOOL(range_contains_internal(fcinfo, r1, r2));
+}
+
+Datum
+range_contains(PG_FUNCTION_ARGS)
+{
+	RangeType	*r1 = PG_GETARG_RANGE(0);
+	RangeType	*r2 = PG_GETARG_RANGE(1);
+
+	PG_RETURN_BOOL(range_contains_internal(fcinfo, r1, r2));
+}
+
+Datum
+elem_contained_by_range(PG_FUNCTION_ARGS)
+{
+	RangeType	*r1		  = PG_GETARG_RANGE(1);
+	RangeType	*r2;
+	Datum		 val	  = PG_GETARG_DATUM(0);
+
+	RangeBound	lower1, lower2;
+	RangeBound	upper1, upper2;
+	bool		empty1;
+
+	range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
+
+	lower2.rngtypid	 = lower1.rngtypid;
+	lower2.inclusive = true;
+	lower2.infinite	 = false;
+	lower2.lower	 = true;
+	lower2.val		 = val;
+
+	upper2.rngtypid	 = lower1.rngtypid;
+	upper2.inclusive = true;
+	upper2.infinite	 = false;
+	upper2.lower	 = false;
+	upper2.val		 = val;
+
+	r2 = DatumGetRangeType(make_range(fcinfo, &lower2, &upper2, false));
+
+	PG_RETURN_BOOL(range_contains_internal(fcinfo, r1, r2));
+}
+
+Datum
+range_contained_by(PG_FUNCTION_ARGS)
+{
+	RangeType	*r1 = PG_GETARG_RANGE(0);
+	RangeType	*r2 = PG_GETARG_RANGE(1);
+
+	PG_RETURN_BOOL(range_contains_internal(fcinfo, r2, r1));
+}
+
+Datum
+range_before(PG_FUNCTION_ARGS)
+{
+	RangeType *r1 = PG_GETARG_RANGE(0);
+	RangeType *r2 = PG_GETARG_RANGE(1);
+
+	RangeBound	lower1, lower2;
+	RangeBound	upper1, upper2;
+	bool		empty1, empty2;
+
+	range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
+	range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
+
+	if (lower1.rngtypid != upper1.rngtypid ||
+		lower1.rngtypid != lower2.rngtypid ||
+		lower1.rngtypid != upper2.rngtypid)
+		elog(ERROR, "range types do not match");
+
+	if (empty1 || empty2)
+		ereport(ERROR,
+				(errcode(ERRCODE_DATA_EXCEPTION),
+				 errmsg("input range is empty")));
+
+	if (range_cmp_bounds(fcinfo, &upper1, &lower2) < 0)
+		PG_RETURN_BOOL(true);
+	else
+		PG_RETURN_BOOL(false);
+}
+
+Datum
+range_after(PG_FUNCTION_ARGS)
+{
+	RangeType *r1 = PG_GETARG_RANGE(0);
+	RangeType *r2 = PG_GETARG_RANGE(1);
+
+	RangeBound	lower1, lower2;
+	RangeBound	upper1, upper2;
+	bool		empty1, empty2;
+
+	range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
+	range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
+
+	if (lower1.rngtypid != upper1.rngtypid ||
+		lower1.rngtypid != lower2.rngtypid ||
+		lower1.rngtypid != upper2.rngtypid)
+		elog(ERROR, "range types do not match");
+
+	if (empty1 || empty2)
+		ereport(ERROR,
+				(errcode(ERRCODE_DATA_EXCEPTION),
+				 errmsg("input range is empty")));
+
+	if (range_cmp_bounds(fcinfo, &lower1, &upper2) > 0)
+		PG_RETURN_BOOL(true);
+	else
+		PG_RETURN_BOOL(false);
+}
+
+Datum range_adjacent(PG_FUNCTION_ARGS)
+{
+	RangeType *r1 = PG_GETARG_RANGE(0);
+	RangeType *r2 = PG_GETARG_RANGE(1);
+
+	RangeTypeInfo rngtypinfo;
+
+	RangeBound	lower1, lower2;
+	RangeBound	upper1, upper2;
+	bool		empty1, empty2;
+
+	range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
+	range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
+
+	if (lower1.rngtypid != upper1.rngtypid ||
+		lower1.rngtypid != lower2.rngtypid ||
+		lower1.rngtypid != upper2.rngtypid)
+		elog(ERROR, "range types do not match");
+
+	if (empty1 || empty2)
+		ereport(ERROR,
+				(errcode(ERRCODE_DATA_EXCEPTION),
+				 errmsg("undefined for empty ranges")));
+
+	/*
+	 * For two ranges to be adjacent, the lower boundary of one range
+	 * has to match the upper boundary of the other. However, the
+	 * inclusivity of those two boundaries must also be different.
+	 *
+	 * The semantics for range_cmp_bounds aren't quite what we need
+	 * here, so we do the comparison more directly.
+	 */
+
+	range_gettypinfo(fcinfo, lower1.rngtypid, &rngtypinfo);
+
+	if (lower1.inclusive != upper2.inclusive)
+	{
+		if (DatumGetInt32(FunctionCall2Coll(&rngtypinfo.cmpFn,
+											rngtypinfo.collation,
+											lower1.val, upper2.val)) == 0)
+			PG_RETURN_BOOL(true);
+	}
+
+	if (upper1.inclusive != lower2.inclusive)
+	{
+		if (DatumGetInt32(FunctionCall2Coll(&rngtypinfo.cmpFn,
+											rngtypinfo.collation,
+											upper1.val, lower2.val)) == 0)
+			PG_RETURN_BOOL(true);
+	}
+
+	PG_RETURN_BOOL(false);
+}
+
+Datum
+range_overlaps(PG_FUNCTION_ARGS)
+{
+	RangeType *r1 = PG_GETARG_RANGE(0);
+	RangeType *r2 = PG_GETARG_RANGE(1);
+
+	RangeBound	lower1, lower2;
+	RangeBound	upper1, upper2;
+	bool		empty1, empty2;
+
+	range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
+	range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
+
+	if (lower1.rngtypid != upper1.rngtypid ||
+		lower1.rngtypid != lower2.rngtypid ||
+		lower1.rngtypid != upper2.rngtypid)
+		elog(ERROR, "range types do not match");
+
+	if (empty1 || empty2)
+		PG_RETURN_BOOL(false);
+
+	if (range_cmp_bounds(fcinfo, &lower1, &lower2) >= 0 &&
+		range_cmp_bounds(fcinfo, &lower1, &upper2) <= 0)
+		PG_RETURN_BOOL(true);
+
+	if (range_cmp_bounds(fcinfo, &lower2, &lower1) >= 0 &&
+		range_cmp_bounds(fcinfo, &lower2, &upper1) <= 0)
+		PG_RETURN_BOOL(true);
+
+	PG_RETURN_BOOL(false);
+}
+
+Datum
+range_overleft(PG_FUNCTION_ARGS)
+{
+	RangeType *r1 = PG_GETARG_RANGE(0);
+	RangeType *r2 = PG_GETARG_RANGE(1);
+
+	RangeBound	lower1, lower2;
+	RangeBound	upper1, upper2;
+	bool		empty1, empty2;
+
+	range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
+	range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
+
+	if (lower1.rngtypid != upper1.rngtypid ||
+		lower1.rngtypid != lower2.rngtypid ||
+		lower1.rngtypid != upper2.rngtypid)
+		elog(ERROR, "range types do not match");
+
+	if (empty1 || empty2)
+		PG_RETURN_BOOL(false);
+
+	if (range_cmp_bounds(fcinfo, &upper1, &upper2) <= 0)
+		PG_RETURN_BOOL(true);
+
+	PG_RETURN_BOOL(false);
+}
+
+Datum
+range_overright(PG_FUNCTION_ARGS)
+{
+	RangeType *r1 = PG_GETARG_RANGE(0);
+	RangeType *r2 = PG_GETARG_RANGE(1);
+
+	RangeBound	lower1, lower2;
+	RangeBound	upper1, upper2;
+	bool		empty1, empty2;
+
+	range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
+	range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
+
+	if (lower1.rngtypid != upper1.rngtypid ||
+		lower1.rngtypid != lower2.rngtypid ||
+		lower1.rngtypid != upper2.rngtypid)
+		elog(ERROR, "range types do not match");
+
+	if (empty1 || empty2)
+		PG_RETURN_BOOL(false);
+
+	if (range_cmp_bounds(fcinfo, &lower1, &lower2) >= 0)
+		PG_RETURN_BOOL(true);
+
+	PG_RETURN_BOOL(false);
+}
+
+
+/* range, range -> range */
+Datum
+range_minus(PG_FUNCTION_ARGS)
+{
+	RangeType *r1 = PG_GETARG_RANGE(0);
+	RangeType *r2 = PG_GETARG_RANGE(1);
+
+	RangeBound	lower1, lower2;
+	RangeBound	upper1, upper2;
+	bool		empty1, empty2;
+
+	int cmp_l1l2, cmp_l1u2, cmp_u1l2, cmp_u1u2;
+
+	range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
+	range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
+
+	if (lower1.rngtypid != upper1.rngtypid ||
+		lower1.rngtypid != lower2.rngtypid ||
+		lower1.rngtypid != upper2.rngtypid)
+		elog(ERROR, "range types do not match");
+
+	if (empty1 || empty2)
+		PG_RETURN_RANGE(r1);
+
+	cmp_l1l2 = range_cmp_bounds(fcinfo, &lower1, &lower2);
+	cmp_l1u2 = range_cmp_bounds(fcinfo, &lower1, &upper2);
+	cmp_u1l2 = range_cmp_bounds(fcinfo, &upper1, &lower2);
+	cmp_u1u2 = range_cmp_bounds(fcinfo, &upper1, &upper2);
+
+	if (cmp_l1l2 < 0 && cmp_u1u2 > 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_DATA_EXCEPTION),
+				 errmsg("result range is not contiguous")));
+
+	if (cmp_l1u2 > 0 || cmp_u1l2 < 0)
+		PG_RETURN_RANGE(r1);
+
+	if (cmp_l1l2 >= 0 && cmp_u1u2 <= 0)
+		PG_RETURN_RANGE(make_empty_range(fcinfo, lower1.rngtypid));
+
+	if (cmp_l1l2 <= 0 && cmp_u1l2 >= 0 && cmp_u1u2 <= 0)
+	{
+		lower2.inclusive = !lower2.inclusive;
+		lower2.lower = false; /* it will become the upper bound */
+		PG_RETURN_RANGE(make_range(fcinfo, &lower1, &lower2, false));
+	}
+
+	if (cmp_l1l2 >= 0 && cmp_u1u2 >= 0 && cmp_l1u2 <= 0)
+	{
+		upper2.inclusive = !upper2.inclusive;
+		upper2.lower = true; /* it will become the lower bound */
+		PG_RETURN_RANGE(make_range(fcinfo, &upper2, &upper1, false));
+	}
+
+	elog(ERROR, "unexpected error in range_minus");
+	PG_RETURN_VOID();
+}
+
+Datum
+range_union(PG_FUNCTION_ARGS)
+{
+	RangeType *r1 = PG_GETARG_RANGE(0);
+	RangeType *r2 = PG_GETARG_RANGE(1);
+
+	RangeBound	 lower1, lower2;
+	RangeBound	 upper1, upper2;
+	bool		 empty1, empty2;
+	RangeBound	*result_lower;
+	RangeBound	*result_upper;
+
+	range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
+	range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
+
+	if (empty1)
+		PG_RETURN_RANGE(r2);
+	if (empty2)
+		PG_RETURN_RANGE(r1);
+
+	if (!DatumGetBool(range_overlaps(fcinfo)) &&
+		!DatumGetBool(range_adjacent(fcinfo)))
+		ereport(ERROR,
+				(errcode(ERRCODE_DATA_EXCEPTION),
+				 errmsg("result range is not contiguous")));
+
+	if (range_cmp_bounds(fcinfo, &lower1, &lower2) < 0)
+		result_lower = &lower1;
+	else
+		result_lower = &lower2;
+
+	if (range_cmp_bounds(fcinfo, &upper1, &upper2) > 0)
+		result_upper = &upper1;
+	else
+		result_upper = &upper2;
+
+	PG_RETURN_RANGE(make_range(fcinfo, result_lower, result_upper, false));
+}
+
+Datum
+range_intersect(PG_FUNCTION_ARGS)
+{
+	RangeType *r1 = PG_GETARG_RANGE(0);
+	RangeType *r2 = PG_GETARG_RANGE(1);
+
+	RangeBound	 lower1, lower2;
+	RangeBound	 upper1, upper2;
+	bool		 empty1, empty2;
+	RangeBound	*result_lower;
+	RangeBound	*result_upper;
+
+	range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
+	range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
+
+	if (empty1 || empty2 || !DatumGetBool(range_overlaps(fcinfo)))
+		PG_RETURN_RANGE(make_empty_range(fcinfo, lower1.rngtypid));
+
+	if (range_cmp_bounds(fcinfo, &lower1, &lower2) >= 0)
+		result_lower = &lower1;
+	else
+		result_lower = &lower2;
+
+	if (range_cmp_bounds(fcinfo, &upper1, &upper2) <= 0)
+		result_upper = &upper1;
+	else
+		result_upper = &upper2;
+
+	PG_RETURN_RANGE(make_range(fcinfo, result_lower, result_upper, false));
+}
+
+/* Btree support */
+
+Datum
+range_cmp(PG_FUNCTION_ARGS)
+{
+	RangeType *r1 = PG_GETARG_RANGE(0);
+	RangeType *r2 = PG_GETARG_RANGE(1);
+
+	RangeBound	lower1, lower2;
+	RangeBound	upper1, upper2;
+	bool		empty1, empty2;
+
+	int cmp;
+
+	range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
+	range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
+
+	if (lower1.rngtypid != upper1.rngtypid ||
+		lower1.rngtypid != lower2.rngtypid ||
+		lower1.rngtypid != upper2.rngtypid)
+		elog(ERROR, "range types do not match");
+
+	if (empty1 && empty2)
+		PG_RETURN_INT32(0);
+	else if (empty1)
+		PG_RETURN_INT32(-1);
+	else if (empty2)
+		PG_RETURN_INT32(1);
+
+	if ((cmp = range_cmp_bounds(fcinfo, &lower1, &lower2)) != 0)
+		PG_RETURN_INT32(cmp);
+
+	PG_RETURN_INT32(range_cmp_bounds(fcinfo, &upper1, &upper2));
+}
+
+Datum
+range_lt(PG_FUNCTION_ARGS)
+{
+	int cmp = range_cmp(fcinfo);
+	PG_RETURN_BOOL(cmp < 0);
+}
+
+Datum
+range_le(PG_FUNCTION_ARGS)
+{
+	int cmp = range_cmp(fcinfo);
+	PG_RETURN_BOOL(cmp <= 0);
+}
+
+Datum
+range_ge(PG_FUNCTION_ARGS)
+{
+	int cmp = range_cmp(fcinfo);
+	PG_RETURN_BOOL(cmp >= 0);
+}
+
+Datum
+range_gt(PG_FUNCTION_ARGS)
+{
+	int cmp = range_cmp(fcinfo);
+	PG_RETURN_BOOL(cmp > 0);
+}
+
+/* Hash support */
+Datum
+hash_range(PG_FUNCTION_ARGS)
+{
+	RangeType	*r			= PG_GETARG_RANGE(0);
+	RangeBound	 lower;
+	RangeBound	 upper;
+	bool		 empty;
+	char		 flags		= 0;
+	uint32		 lower_hash = 0;
+	uint32		 upper_hash = 0;
+	uint32		 result		= 0;
+
+	RangeTypeInfo rngtypinfo;
+
+	TypeCacheEntry *typentry;
+	Oid subtype;
+	FunctionCallInfoData locfcinfo;
+
+
+	range_deserialize(fcinfo, r, &lower, &upper, &empty);
+
+	if (lower.rngtypid != upper.rngtypid)
+		elog(ERROR, "range types do not match");
+
+	if (empty)
+		flags |= RANGE_EMPTY;
+
+	flags |= (lower.inclusive)	? RANGE_LB_INC  : 0;
+	flags |= (lower.infinite)	? RANGE_LB_INF  : 0;
+	flags |= (upper.inclusive)	? RANGE_UB_INC  : 0;
+	flags |= (upper.infinite)	? RANGE_UB_INF  : 0;
+
+	range_gettypinfo(fcinfo, lower.rngtypid, &rngtypinfo);
+	subtype = rngtypinfo.subtype;
+
+	/*
+	 * We arrange to look up the hash function only once per series of
+	 * calls, assuming the subtype doesn't change underneath us.  The
+	 * typcache is used so that we have no memory leakage when being
+	 * used as an index support function.
+	 */
+	typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
+	if (typentry == NULL || typentry->type_id != subtype)
+	{
+		typentry = lookup_type_cache(subtype, TYPECACHE_HASH_PROC_FINFO);
+		if (!OidIsValid(typentry->hash_proc_finfo.fn_oid))
+			ereport(ERROR,
+					(errcode(ERRCODE_UNDEFINED_FUNCTION),
+					 errmsg("could not identify a hash function for type %s",
+							format_type_be(subtype))));
+		fcinfo->flinfo->fn_extra = (void *) typentry;
+	}
+
+	/*
+	 * Apply the hash function to each bound (the hash function shouldn't
+	 * care about the collation).
+	 */
+	InitFunctionCallInfoData(locfcinfo, &typentry->hash_proc_finfo, 1,
+							 InvalidOid, NULL, NULL);
+
+	if (RANGE_HAS_LBOUND(flags))
+	{
+		locfcinfo.arg[0]	 = lower.val;
+		locfcinfo.argnull[0] = false;
+		locfcinfo.isnull	 = false;
+		lower_hash = DatumGetUInt32(FunctionCallInvoke(&locfcinfo));
+	}
+	if (RANGE_HAS_UBOUND(flags))
+	{
+		locfcinfo.arg[0]	 = upper.val;
+		locfcinfo.argnull[0] = false;
+		locfcinfo.isnull	 = false;
+		upper_hash = DatumGetUInt32(FunctionCallInvoke(&locfcinfo));
+	}
+
+	result	= hash_uint32((uint32) flags);
+	result ^= lower_hash;
+	result	= (result << 1) | (result >> 31);
+	result ^= upper_hash;
+
+	PG_RETURN_INT32(result);
+}
+
+/*
+ *----------------------------------------------------------
+ * CANONICAL FUNCTIONS
+ *
+ *   Functions for specific built-in range types.
+ *----------------------------------------------------------
+ */
+
+Datum
+int4range_canonical(PG_FUNCTION_ARGS)
+{
+	RangeType	*r = PG_GETARG_RANGE(0);
+
+	RangeBound	lower;
+	RangeBound	upper;
+	bool		empty;
+
+	range_deserialize(fcinfo, r, &lower, &upper, &empty);
+
+	if (empty)
+		PG_RETURN_RANGE(r);
+
+	if (!lower.infinite && !lower.inclusive)
+	{
+		lower.val = DirectFunctionCall2(int4pl, lower.val, Int32GetDatum(1));
+		lower.inclusive = true;
+	}
+
+	if (!upper.infinite && upper.inclusive)
+	{
+		upper.val = DirectFunctionCall2(int4pl, upper.val, Int32GetDatum(1));
+		upper.inclusive = false;
+	}
+
+	PG_RETURN_RANGE(range_serialize(fcinfo, &lower, &upper, false));
+}
+
+Datum
+int8range_canonical(PG_FUNCTION_ARGS)
+{
+	RangeType	*r = PG_GETARG_RANGE(0);
+
+	RangeBound	lower;
+	RangeBound	upper;
+	bool		empty;
+
+	range_deserialize(fcinfo, r, &lower, &upper, &empty);
+
+	if (empty)
+		PG_RETURN_RANGE(r);
+
+	if (!lower.infinite && !lower.inclusive)
+	{
+		lower.val = DirectFunctionCall2(int8pl, lower.val, Int64GetDatum(1));
+		lower.inclusive = true;
+	}
+
+	if (!upper.infinite && upper.inclusive)
+	{
+		upper.val = DirectFunctionCall2(int8pl, upper.val, Int64GetDatum(1));
+		upper.inclusive = false;
+	}
+
+	PG_RETURN_RANGE(range_serialize(fcinfo, &lower, &upper, false));
+}
+
+Datum
+daterange_canonical(PG_FUNCTION_ARGS)
+{
+	RangeType	*r = PG_GETARG_RANGE(0);
+
+	RangeBound	lower;
+	RangeBound	upper;
+	bool		empty;
+
+	range_deserialize(fcinfo, r, &lower, &upper, &empty);
+
+	if (empty)
+		PG_RETURN_RANGE(r);
+
+	if (!lower.infinite && !lower.inclusive)
+	{
+		lower.val = DirectFunctionCall2(date_pli, lower.val, Int32GetDatum(1));
+		lower.inclusive = true;
+	}
+
+	if (!upper.infinite && upper.inclusive)
+	{
+		upper.val = DirectFunctionCall2(date_pli, upper.val, Int32GetDatum(1));
+		upper.inclusive = false;
+	}
+
+	PG_RETURN_RANGE(range_serialize(fcinfo, &lower, &upper, false));
+}
+
+/*
+ *----------------------------------------------------------
+ * SUBTYPE_DIFF FUNCTIONS
+ *
+ *   Functions for specific built-in range types.
+ *----------------------------------------------------------
+ */
+
+Datum
+int4range_subdiff(PG_FUNCTION_ARGS)
+{
+	int32 v1 = PG_GETARG_INT32(0);
+	int32 v2 = PG_GETARG_INT32(1);
+
+	PG_RETURN_FLOAT8((float8)(v1-v2));
+}
+
+Datum
+int8range_subdiff(PG_FUNCTION_ARGS)
+{
+	int64 v1 = PG_GETARG_INT64(0);
+	int64 v2 = PG_GETARG_INT64(1);
+
+	PG_RETURN_FLOAT8((float8)(v1-v2));
+}
+
+Datum
+daterange_subdiff(PG_FUNCTION_ARGS)
+{
+	int32 v1 = PG_GETARG_INT32(0);
+	int32 v2 = PG_GETARG_INT32(1);
+
+	PG_RETURN_FLOAT8((float8)(v1-v2));
+}
+
+Datum
+numrange_subdiff(PG_FUNCTION_ARGS)
+{
+	Datum		v1 = PG_GETARG_DATUM(0);
+	Datum		v2 = PG_GETARG_DATUM(1);
+	Datum		numresult;
+	float8		floatresult;
+
+	numresult = DirectFunctionCall2(numeric_sub, v1, v2);
+
+	floatresult = DatumGetFloat8(
+		DirectFunctionCall1(numeric_float8, numresult));
+
+	PG_RETURN_FLOAT8(floatresult);
+}
+
+Datum
+tsrange_subdiff(PG_FUNCTION_ARGS)
+{
+	Timestamp	v1 = PG_GETARG_TIMESTAMP(0);
+	Timestamp	v2 = PG_GETARG_TIMESTAMP(1);
+	float8		result;
+
+#ifdef HAVE_INT64_TIMESTAMP
+	result = ((float8)(v1-v2)) / USECS_PER_SEC;
+#else
+	result = timestamp;
+#endif
+
+	PG_RETURN_FLOAT8(result);
+}
+
+Datum
+tstzrange_subdiff(PG_FUNCTION_ARGS)
+{
+	Timestamp	v1 = PG_GETARG_TIMESTAMP(0);
+	Timestamp	v2 = PG_GETARG_TIMESTAMP(1);
+	float8		result;
+
+#ifdef HAVE_INT64_TIMESTAMP
+	result = ((float8)(v1-v2)) / USECS_PER_SEC;
+#else
+	result = timestamp;
+#endif
+
+	PG_RETURN_FLOAT8(result);
+}
+
+/*
+ *----------------------------------------------------------
+ * SUPPORT FUNCTIONS
+ *
+ *   These functions aren't in pg_proc, but are useful if
+ *   defining new generic range functions in C.
+ *----------------------------------------------------------
+ */
+
+/*
+ * Serialized format is:
+ *
+ *  4 bytes: Range type Oid
+ *  Lower boundary, if any, aligned according to subtype's typalign
+ *  Upper boundary, if any, aligned according to subtype's typalign
+ *  1 byte for flags
+ *
+ * This representation is chosen to be compact when the boundary
+ * values need to be MAXALIGNed. A palloc chunk always starts out
+ * MAXALIGNed, and the first 4 bytes will be the length header (range
+ * types are always variable-length), then the next 4 bytes will be
+ * the range type Oid. That leaves the first boundary item MAXALIGNed
+ * without the need for padding.
+ *
+ * However, it requires a slightly odd deserialization strategy,
+ * because we have to read the flags byte before we know whether to
+ * read a boundary value.
+ */
+
+/*
+ * This serializes a range, but does not canonicalize it. This should
+ * only be called by a canonicalization function.
+ */
+Datum
+range_serialize(FunctionCallInfo fcinfo, RangeBound *lower, RangeBound *upper,
+				bool empty)
+{
+	Datum		range;
+	size_t		msize;
+	Pointer		ptr;
+	int16		typlen;
+	char		typalign;
+	bool		typbyval;
+	char		typstorage;
+	char		flags	   = 0;
+
+	RangeTypeInfo rngtypinfo;
+
+	if (lower->rngtypid != upper->rngtypid)
+		elog(ERROR, "range types do not match");
+
+	range_gettypinfo(fcinfo, lower->rngtypid, &rngtypinfo);
+
+	typlen	   = rngtypinfo.subtyplen;
+	typalign   = rngtypinfo.subtypalign;
+	typbyval   = rngtypinfo.subtypbyval;
+	typstorage = rngtypinfo.subtypstorage;
+
+	if (empty)
+		flags |= RANGE_EMPTY;
+	else if (range_cmp_bounds(fcinfo, lower, upper) > 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_DATA_EXCEPTION),
+				 errmsg("range lower bound must be less than or equal to range upper bound")));
+
+	flags |= (lower->inclusive) ? RANGE_LB_INC  : 0;
+	flags |= (lower->infinite)  ? RANGE_LB_INF  : 0;
+	flags |= (upper->inclusive) ? RANGE_UB_INC  : 0;
+	flags |= (upper->infinite)  ? RANGE_UB_INF  : 0;
+
+	msize  = VARHDRSZ;
+	msize += sizeof(Oid);
+
+	if (RANGE_HAS_LBOUND(flags))
+	{
+		msize = datum_compute_size(msize, lower->val, typbyval, typalign,
+								   typlen, typstorage);
+	}
+
+	if (RANGE_HAS_UBOUND(flags))
+	{
+		msize = datum_compute_size(msize, upper->val, typbyval, typalign,
+								   typlen, typstorage);
+	}
+
+	msize += sizeof(char);
+
+	ptr = palloc0(msize);
+	range = (Datum) ptr;
+
+	ptr += VARHDRSZ;
+
+	memcpy(ptr, &lower->rngtypid, sizeof(Oid));
+	ptr += sizeof(Oid);
+
+	if (RANGE_HAS_LBOUND(flags))
+	{
+		Assert(lower->lower);
+		ptr = datum_write(ptr, lower->val, typbyval, typalign, typlen,
+						  typstorage);
+	}
+
+	if (RANGE_HAS_UBOUND(flags))
+	{
+		Assert(!upper->lower);
+		ptr = datum_write(ptr, upper->val, typbyval, typalign, typlen,
+						  typstorage);
+	}
+
+	memcpy(ptr, &flags, sizeof(char));
+	ptr += sizeof(char);
+
+	SET_VARSIZE(range, msize);
+	PG_RETURN_RANGE(range);
+}
+
+void
+range_deserialize(FunctionCallInfo fcinfo, RangeType *range, RangeBound *lower,
+				  RangeBound *upper, bool *empty)
+{
+	Pointer		ptr	= VARDATA(range);
+	char		typalign;
+	int16		typlen;
+	int16		typbyval;
+	char		flags;
+	Oid			rngtypid;
+	Datum		lbound;
+	Datum		ubound;
+	Pointer		flags_ptr;
+
+	RangeTypeInfo rngtypinfo;
+
+	memset(lower, 0, sizeof(RangeBound));
+	memset(upper, 0, sizeof(RangeBound));
+
+	/* peek at last byte to read the flag byte */
+	flags_ptr = ptr + VARSIZE(range) - VARHDRSZ - 1;
+	memcpy(&flags, flags_ptr, sizeof(char));
+
+	memcpy(&rngtypid, ptr, sizeof(Oid));
+	ptr += sizeof(Oid);
+
+	if (rngtypid == ANYRANGEOID)
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("cannot output a value of type anyrange")));
+
+	range_gettypinfo(fcinfo, rngtypid, &rngtypinfo);
+
+	typalign = rngtypinfo.subtypalign;
+	typlen	 = rngtypinfo.subtyplen;
+	typbyval = rngtypinfo.subtypbyval;
+
+	if (RANGE_HAS_LBOUND(flags))
+	{
+		ptr = (Pointer) att_align_pointer(ptr, typalign, typlen, ptr);
+		lbound = fetch_att(ptr, typbyval, typlen);
+		ptr = (Pointer) att_addlength_datum(ptr, typlen, PointerGetDatum(ptr));
+		if (typlen == -1)
+			lbound = PointerGetDatum(PG_DETOAST_DATUM(lbound));
+	}
+	else
+		lbound = (Datum) 0;
+
+	if (RANGE_HAS_UBOUND(flags))
+	{
+		ptr = (Pointer) att_align_pointer(ptr, typalign, typlen, ptr);
+		ubound = fetch_att(ptr, typbyval, typlen);
+		ptr = (Pointer) att_addlength_datum(ptr, typlen, PointerGetDatum(ptr));
+		if (typlen == -1)
+			ubound = PointerGetDatum(PG_DETOAST_DATUM(ubound));
+	}
+	else
+		ubound = (Datum) 0;
+
+	*empty = flags & RANGE_EMPTY;
+
+	lower->rngtypid	 = rngtypid;
+	lower->val		 = lbound;
+	lower->inclusive = flags &	RANGE_LB_INC;
+	lower->infinite	 = flags &	RANGE_LB_INF;
+	lower->lower	 = true;
+
+	upper->rngtypid	 = rngtypid;
+	upper->val		 = ubound;
+	upper->inclusive = flags &	RANGE_UB_INC;
+	upper->infinite	 = flags &	RANGE_UB_INF;
+	upper->lower	 = false;
+}
+
+/*
+ * This both serializes and caonicalizes (if applicable) the
+ * range. This should be used by most callers.
+ */
+Datum
+make_range(FunctionCallInfo fcinfo, RangeBound *lower, RangeBound *upper,
+		   bool empty)
+{
+	Datum range;
+
+	RangeTypeInfo rngtypinfo;
+
+	range_gettypinfo(fcinfo, lower->rngtypid, &rngtypinfo);
+
+	if (lower->rngtypid != upper->rngtypid)
+		elog(ERROR, "range types do not match");
+
+	range = range_serialize(fcinfo, lower, upper, empty);
+
+	if (rngtypinfo.canonicalFn.fn_addr != NULL)
+		range = FunctionCall1(&rngtypinfo.canonicalFn, range);
+
+	PG_RETURN_RANGE(range);
+}
+
+int
+range_cmp_bounds(FunctionCallInfo fcinfo, RangeBound *b1, RangeBound *b2)
+{
+	int			result;
+
+	RangeTypeInfo rngtypinfo;
+
+	if (b1->infinite && b2->infinite)
+	{
+		if (b1->lower == b2->lower)
+			return 0;
+		else
+			return (b1->lower) ? -1 : 1;
+	}
+	else if (b1->infinite && !b2->infinite)
+		return (b1->lower) ? -1 : 1;
+	else if (!b1->infinite && b2->infinite)
+		return (b2->lower) ? 1 : -1;
+
+	range_gettypinfo(fcinfo, b1->rngtypid, &rngtypinfo);
+	result = DatumGetInt32(FunctionCall2Coll(&rngtypinfo.cmpFn,
+											 rngtypinfo.collation,
+											 b1->val, b2->val));
+
+	if (result == 0)
+	{
+		if (b1->inclusive && !b2->inclusive)
+			return (b2->lower) ? -1 : 1;
+		else if (!b1->inclusive && b2->inclusive)
+			return (b1->lower) ? 1 : -1;
+	}
+
+	return result;
+}
+
+RangeType *
+make_empty_range(FunctionCallInfo fcinfo, Oid rngtypid)
+{
+	RangeBound lower;
+	RangeBound upper;
+
+	memset(&lower, 0, sizeof(RangeBound));
+	memset(&upper, 0, sizeof(RangeBound));
+
+	lower.rngtypid = rngtypid;
+	lower.lower = true;
+	upper.rngtypid = rngtypid;
+	upper.lower = false;
+
+	return DatumGetRangeType(make_range(fcinfo, &lower, &upper, true));
+}
+
+/*
+ * Fills in rngtypinfo, from a cached copy if available.
+ */
+void
+range_gettypinfo(FunctionCallInfo fcinfo, Oid rngtypid,
+				 RangeTypeInfo *rngtypinfo)
+{
+	RangeTypeInfo *cached = (RangeTypeInfo *) fcinfo->flinfo->fn_extra;
+
+	if (cached == NULL)
+	{
+		fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+													  sizeof(RangeTypeInfo));
+		cached = (RangeTypeInfo *) fcinfo->flinfo->fn_extra;
+		cached->rngtypid = ~rngtypid;
+	}
+
+	if (cached->rngtypid != rngtypid)
+	{
+		Form_pg_range		pg_range;
+		Form_pg_opclass		pg_opclass;
+		Form_pg_type		pg_type;
+		HeapTuple			tup;
+
+		Oid		subtypeOid;
+		Oid		collationOid;
+		Oid		canonicalOid;
+		Oid		subdiffOid;
+		Oid		opclassOid;
+		Oid		cmpFnOid;
+		Oid		opfamilyOid;
+		Oid		opcintype;
+		int16	subtyplen;
+		char	subtypalign;
+		char	subtypstorage;
+		bool	subtypbyval;
+
+		/* get information from pg_range */
+		tup = SearchSysCache1(RANGETYPE, ObjectIdGetDatum(rngtypid));
+		if (!HeapTupleIsValid(tup))
+			elog(ERROR, "cache lookup failed for range type %u", rngtypid);
+
+		pg_range = (Form_pg_range) GETSTRUCT(tup);
+
+		subtypeOid	 = pg_range->rngsubtype;
+		collationOid = pg_range->rngcollation;
+		canonicalOid = pg_range->rngcanonical;
+		opclassOid	 = pg_range->rngsubopc;
+		subdiffOid	 = pg_range->rngsubdiff;
+
+		ReleaseSysCache(tup);
+
+		/* get information from pg_opclass */
+		tup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassOid));
+		if (!HeapTupleIsValid(tup))
+			ereport(ERROR,
+					(errcode(ERRCODE_UNDEFINED_OBJECT),
+					 errmsg("operator class with OID %u does not exist",
+							opclassOid)));
+
+		pg_opclass = (Form_pg_opclass) GETSTRUCT(tup);
+
+		opfamilyOid = pg_opclass->opcfamily;
+		opcintype  = pg_opclass->opcintype;
+
+		ReleaseSysCache(tup);
+
+		cmpFnOid = get_opfamily_proc(opfamilyOid, opcintype, opcintype,
+									 BTORDER_PROC);
+
+		if (!OidIsValid(cmpFnOid))
+			elog(ERROR, "unable to find compare function for operator class %d",
+				 opclassOid);
+
+		/* get information from pg_type */
+		tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(subtypeOid));
+		if (!HeapTupleIsValid(tup))
+			elog(ERROR, "cache lookup failed for type %u", subtypeOid);
+
+		pg_type = (Form_pg_type) GETSTRUCT(tup);
+
+		subtyplen	  = pg_type->typlen;
+		subtypalign	  = pg_type->typalign;
+		subtypstorage = pg_type->typstorage;
+		subtypbyval	  = pg_type->typbyval;
+
+		ReleaseSysCache(tup);
+
+		/* set up the cache */
+
+		if (OidIsValid(canonicalOid))
+			fmgr_info(canonicalOid, &cached->canonicalFn);
+		else
+			cached->canonicalFn.fn_addr = NULL;
+
+		if (OidIsValid(subdiffOid))
+			fmgr_info(subdiffOid, &cached->subdiffFn);
+		else
+			cached->subdiffFn.fn_addr = NULL;
+
+		fmgr_info(cmpFnOid, &cached->cmpFn);
+		cached->subtype		  = subtypeOid;
+		cached->collation	  = collationOid;
+		cached->subtyplen	  = subtyplen;
+		cached->subtypalign	  = subtypalign;
+		cached->subtypstorage = subtypstorage;
+		cached->subtypbyval	  = subtypbyval;
+		cached->rngtypid	  = rngtypid;
+	}
+
+	memcpy(rngtypinfo, cached, sizeof(RangeTypeInfo));
+}
+
+/*
+ * Given a string representing the flags for the range type, return the flags
+ * represented as a char.
+ *
+ * Exported so that it can be called by DefineRange to check the default flags.
+ */
+char
+range_parse_flags(char *flags_str)
+{
+	char		 flags = 0;
+
+	if (flags_str[0] == '\0' ||
+		flags_str[1] == '\0' ||
+		flags_str[2] != '\0')
+		ereport(ERROR,
+				(errcode(ERRCODE_SYNTAX_ERROR),
+				 errmsg("invalid range bound flags"),
+				 errhint("Valid values are '[]', '[)', '(]', and '()'.")));
+
+	switch (flags_str[0])
+	{
+		case '[':
+			flags |= RANGE_LB_INC;
+			break;
+		case '(':
+			break;
+		default:
+			ereport(ERROR,
+					(errcode(ERRCODE_SYNTAX_ERROR),
+					 errmsg("invalid range bound flags"),
+					 errhint("Valid values are '[]', '[)', '(]', and '()'.")));
+	}
+
+	switch (flags_str[1])
+	{
+		case ']':
+			flags |= RANGE_UB_INC;
+			break;
+		case ')':
+			break;
+		default:
+			ereport(ERROR,
+					(errcode(ERRCODE_SYNTAX_ERROR),
+					 errmsg("invalid range bound flags"),
+					 errhint("Valid values are '[]', '[)', '(]', and '()'.")));
+	}
+
+	return flags;
+}
+
+/*
+ *----------------------------------------------------------
+ * STATIC FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+/*
+ * Parse range input, modeled after record_in in rowtypes.c.
+ *
+ *  <range>   := EMPTY
+ *	           | <lb-inc> <string>, <string> <ub-inc>
+ *  <lb-inc>  := '[' | '('
+ *  <ub-inc>  := ']' | ')'
+ *
+ * Whitespace before or after <range> is ignored. Whitespace within a <string>
+ * is taken literally and becomes the input string for that bound.
+ *
+ * A <string> of length zero is taken as "infinite" (i.e. no bound); unless it
+ * is surrounded by double-quotes, in which case it is the literal empty
+ * string.
+ *
+ * Within a <string>, special characters (such as comma, parenthesis, or
+ * brackets) can be enclosed in double-quotes or escaped with backslash. Within
+ * double-quotes, a double-quote can be escaped with double-quote or backslash.
+ */
+static void
+range_parse(char *string,  char *flags, char **lbound_str,
+			char **ubound_str)
+{
+	char		*ptr = string;
+	bool		 infinite;
+
+	*flags = 0;
+
+	/* consume whitespace */
+	while (*ptr != '\0' && isspace(*ptr))
+		ptr++;
+
+	/* check for empty range */
+	if (pg_strncasecmp(ptr, RANGE_EMPTY_LITERAL,
+					   strlen(RANGE_EMPTY_LITERAL)) == 0)
+	{
+		*flags = RANGE_EMPTY;
+
+		ptr += strlen(RANGE_EMPTY_LITERAL);
+
+		/* the rest should be whitespace */
+		while (*ptr != '\0' && isspace(*ptr))
+			ptr++;
+
+		/* should have consumed everything */
+		if (*ptr != '\0')
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+					 errmsg("malformed range literal: \"%s\"",
+							string),
+					 errdetail("Unexpected end of input.")));
+
+		return;
+	}
+
+	if (*ptr == '[' || *ptr == '(')
+	{
+		if (*ptr == '[')
+			*flags |= RANGE_LB_INC;
+		ptr++;
+	}
+	else
+	{
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+				 errmsg("malformed range literal: \"%s\"",
+						string),
+				 errdetail("Missing left parenthesis or bracket.")));
+	}
+
+	ptr = range_parse_bound(string, ptr, lbound_str, &infinite);
+	if (infinite)
+	{
+		*flags |= RANGE_LB_INF;
+		*flags &= ~RANGE_LB_INC;
+	}
+
+	if (*ptr != ',')
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+				 errmsg("malformed range literal: \"%s\"",
+						string),
+				 errdetail("Missing upper bound.")));
+	ptr++;
+
+	ptr = range_parse_bound(string, ptr, ubound_str, &infinite);
+
+	if (*ptr == ')' || *ptr == ']')
+	{
+		if (*ptr == ']')
+			*flags |= RANGE_UB_INC;
+	}
+	else
+	{
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+				 errmsg("malformed range literal: \"%s\"",
+						string),
+				 errdetail("Too many boundaries.")));
+	}
+
+	ptr++;
+
+	if (infinite)
+	{
+		*flags |= RANGE_UB_INF;
+		*flags &= ~RANGE_UB_INC;
+	}
+
+	/* consume whitespace */
+	while (*ptr != '\0' && isspace(*ptr))
+		ptr++;
+
+	if (*ptr != '\0')
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+				 errmsg("malformed range literal: \"%s\"",
+						string),
+				 errdetail("Junk after right parenthesis or bracket.")));
+
+	return;
+}
+
+static char *
+range_parse_bound(char *string, char *ptr, char **bound_str, bool *infinite)
+{
+	StringInfoData		 buf;
+
+	/* Check for null: completely empty input means null */
+	if (*ptr == ',' || *ptr == ')' || *ptr == ']')
+	{
+		*bound_str = NULL;
+		*infinite  = true;
+	}
+	else
+	{
+		/* Extract string for this column */
+		bool		inquote = false;
+
+		initStringInfo(&buf);
+		while (inquote || !(*ptr == ',' || *ptr == ')' || *ptr == ']'))
+		{
+			char		ch = *ptr++;
+
+			if (ch == '\0')
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+						 errmsg("malformed range literal: \"%s\"",
+								string),
+						 errdetail("Unexpected end of input.")));
+			if (ch == '\\')
+			{
+				if (*ptr == '\0')
+					ereport(ERROR,
+							(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+							 errmsg("malformed range literal: \"%s\"",
+									string),
+							 errdetail("Unexpected end of input.")));
+				appendStringInfoChar(&buf, *ptr++);
+			}
+			else if (ch == '\"')
+			{
+				if (!inquote)
+					inquote = true;
+				else if (*ptr == '\"')
+				{
+					/* doubled quote within quote sequence */
+					appendStringInfoChar(&buf, *ptr++);
+				}
+				else
+					inquote = false;
+			}
+			else
+				appendStringInfoChar(&buf, ch);
+		}
+
+		*bound_str = buf.data;
+		*infinite  = false;
+	}
+
+	return ptr;
+}
+
+static char *
+range_deparse(char flags, char *lbound_str, char *ubound_str)
+{
+	StringInfoData	 buf;
+
+	initStringInfo(&buf);
+
+	if (flags & RANGE_EMPTY)
+		return pstrdup(RANGE_EMPTY_LITERAL);
+
+	appendStringInfoString(&buf, (flags & RANGE_LB_INC) ? "[" : "(");
+
+	if (RANGE_HAS_LBOUND(flags))
+		appendStringInfoString(&buf, range_bound_escape(lbound_str));
+
+	appendStringInfoString(&buf, ",");
+
+	if (RANGE_HAS_UBOUND(flags))
+		appendStringInfoString(&buf, range_bound_escape(ubound_str));
+
+	appendStringInfoString(&buf, (flags & RANGE_UB_INC) ? "]" : ")");
+
+	return buf.data;
+}
+
+static char *
+range_bound_escape(char *value)
+{
+	bool				 nq;
+	char				*tmp;
+	StringInfoData		 buf;
+
+	initStringInfo(&buf);
+
+	/* Detect whether we need double quotes for this value */
+	nq = (value[0] == '\0');	/* force quotes for empty string */
+	for (tmp = value; *tmp; tmp++)
+	{
+		char		ch = *tmp;
+
+		if (ch == '"' || ch == '\\' ||
+			ch == '(' || ch == ')' ||
+			ch == '[' || ch == ']' ||
+			ch == ',' ||
+			isspace((unsigned char) ch))
+		{
+			nq = true;
+			break;
+		}
+	}
+
+	/* And emit the string */
+	if (nq)
+		appendStringInfoChar(&buf, '"');
+	for (tmp = value; *tmp; tmp++)
+	{
+		char		ch = *tmp;
+
+		if (ch == '"' || ch == '\\')
+			appendStringInfoChar(&buf, ch);
+		appendStringInfoChar(&buf, ch);
+	}
+	if (nq)
+		appendStringInfoChar(&buf, '"');
+
+	return buf.data;
+}
+
+static bool
+range_contains_internal(FunctionCallInfo fcinfo, RangeType *r1, RangeType *r2)
+{
+	RangeBound	lower1;
+	RangeBound	upper1;
+	bool		empty1;
+	RangeBound	lower2;
+	RangeBound	upper2;
+	bool		empty2;
+
+	range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
+	range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
+
+	if (lower1.rngtypid != upper1.rngtypid ||
+		lower1.rngtypid != lower2.rngtypid ||
+		lower1.rngtypid != upper2.rngtypid)
+		elog(ERROR, "range types do not match");
+
+	if (empty2)
+		return true;
+	else if (empty1)
+		return false;
+
+	if (range_cmp_bounds(fcinfo, &lower1, &lower2) > 0)
+		return false;
+	if (range_cmp_bounds(fcinfo, &upper1, &upper2) < 0)
+		return false;
+
+	return true;
+}
+
+/*
+ * datum_compute_size() and datum_write() are modeled after
+ * heap_compute_data_size() and heap_fill_tuple().
+ */
+
+static Size
+datum_compute_size(Size data_length, Datum val, bool typbyval, char typalign,
+				   int16 typlen, char typstorage)
+{
+	if (TYPE_IS_PACKABLE(typlen, typstorage) &&
+		VARATT_CAN_MAKE_SHORT(DatumGetPointer(val)))
+	{
+		/*
+		 * we're anticipating converting to a short varlena header, so
+		 * adjust length and don't count any alignment
+		 */
+		data_length += VARATT_CONVERTED_SHORT_SIZE(DatumGetPointer(val));
+	}
+	else
+	{
+		data_length = att_align_datum(data_length, typalign, typlen, val);
+		data_length = att_addlength_datum(data_length, typlen, val);
+	}
+
+	return data_length;
+}
+
+/*
+ * Modified version of the code in heap_fill_tuple(). Writes the datum to ptr
+ * using the correct alignment, and also uses short varlena header if
+ * applicable.
+ */
+static Pointer
+datum_write(Pointer ptr, Datum datum, bool typbyval, char typalign,
+			int16 typlen, char typstorage)
+{
+	Size		data_length;
+
+	if (typbyval)
+	{
+		/* pass-by-value */
+		ptr = (char *) att_align_nominal(ptr, typalign);
+		store_att_byval(ptr, datum, typlen);
+		data_length = typlen;
+	}
+	else if (typlen == -1)
+	{
+		/* varlena */
+		Pointer		val = DatumGetPointer(datum);
+
+		if (VARATT_IS_EXTERNAL(val))
+		{
+			/* no alignment, since it's short by definition */
+			data_length = VARSIZE_EXTERNAL(val);
+			memcpy(ptr, val, data_length);
+		}
+		else if (VARATT_IS_SHORT(val))
+		{
+			/* no alignment for short varlenas */
+			data_length = VARSIZE_SHORT(val);
+			memcpy(ptr, val, data_length);
+		}
+		else if (TYPE_IS_PACKABLE(typlen, typstorage) &&
+				 VARATT_CAN_MAKE_SHORT(val))
+		{
+			/* convert to short varlena -- no alignment */
+			data_length = VARATT_CONVERTED_SHORT_SIZE(val);
+			SET_VARSIZE_SHORT(ptr, data_length);
+			memcpy(ptr + 1, VARDATA(val), data_length - 1);
+		}
+		else
+		{
+			/* full 4-byte header varlena */
+			ptr = (char *) att_align_nominal(ptr, typalign);
+			data_length = VARSIZE(val);
+			memcpy(ptr, val, data_length);
+		}
+	}
+	else if (typlen == -2)
+	{
+		/* cstring ... never needs alignment */
+		Assert(typalign == 'c');
+		data_length = strlen(DatumGetCString(datum)) + 1;
+		memcpy(ptr, DatumGetPointer(datum), data_length);
+	}
+	else
+	{
+		/* fixed-length pass-by-reference */
+		ptr = (char *) att_align_nominal(ptr, typalign);
+		Assert(typlen > 0);
+		data_length = typlen;
+		memcpy(ptr, DatumGetPointer(datum), data_length);
+	}
+
+	ptr += data_length;
+
+	return ptr;
+}
diff --git a/src/backend/utils/adt/rangetypes_gist.c b/src/backend/utils/adt/rangetypes_gist.c
new file mode 100644
index 0000000000000000000000000000000000000000..9dc7fd1b5a530fd24a57faf3f385ac433c9c17d9
--- /dev/null
+++ b/src/backend/utils/adt/rangetypes_gist.c
@@ -0,0 +1,587 @@
+/*-------------------------------------------------------------------------
+ *
+ * rangetypes_gist.c
+ *	  GiST support for range types.
+ *
+ * Copyright (c) 2006-2011, PostgreSQL Global Development Group
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/utils/adt/rangetypes_gist.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/gist.h"
+#include "access/skey.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/lsyscache.h"
+#include "utils/rangetypes.h"
+
+#define RANGESTRAT_EQ					1
+#define RANGESTRAT_NE					2
+#define RANGESTRAT_OVERLAPS				3
+#define RANGESTRAT_CONTAINS_ELEM		4
+#define RANGESTRAT_ELEM_CONTAINED_BY	5
+#define RANGESTRAT_CONTAINS				6
+#define RANGESTRAT_CONTAINED_BY			7
+#define RANGESTRAT_BEFORE				8
+#define RANGESTRAT_AFTER				9
+#define RANGESTRAT_OVERLEFT				10
+#define RANGESTRAT_OVERRIGHT			11
+#define RANGESTRAT_ADJACENT				12
+
+static RangeType *range_super_union(FunctionCallInfo fcinfo, RangeType *r1,
+									RangeType *r2);
+static bool range_gist_consistent_int(FunctionCallInfo fcinfo,
+									  StrategyNumber strategy, RangeType *key,
+									  RangeType *query);
+static bool range_gist_consistent_leaf(FunctionCallInfo fcinfo,
+									   StrategyNumber strategy, RangeType *key,
+									   RangeType *query);
+static int sort_item_cmp(const void *a, const void *b);
+
+/*
+ * Auxiliary structure for picksplit method.
+ */
+typedef struct
+{
+	int					 index;
+	RangeType			*data;
+	FunctionCallInfo	 fcinfo;
+} PickSplitSortItem;
+
+
+Datum
+range_gist_consistent(PG_FUNCTION_ARGS)
+{
+	GISTENTRY			*entry	  = (GISTENTRY *) PG_GETARG_POINTER(0);
+	Datum				 dquery	  = PG_GETARG_DATUM(1);
+	StrategyNumber		 strategy = (StrategyNumber) PG_GETARG_UINT16(2);
+	/* Oid subtype = PG_GETARG_OID(3); */
+	bool				*recheck  = (bool *) PG_GETARG_POINTER(4);
+	RangeType			*key	  = DatumGetRangeType(entry->key);
+	RangeType			*query;
+
+	RangeBound	lower;
+	RangeBound	upper;
+	bool		empty;
+	Oid			rngtypid;
+
+	*recheck = false;
+	range_deserialize(fcinfo, key, &lower, &upper, &empty);
+	rngtypid = lower.rngtypid;
+
+	switch (strategy)
+	{
+		RangeBound lower;
+		RangeBound upper;
+
+		/*
+		 * For contains and contained by operators, the other operand is a
+		 * "point" of the subtype. Construct a singleton range containing just
+		 * that value.
+		 */
+		case RANGESTRAT_CONTAINS_ELEM:
+		case RANGESTRAT_ELEM_CONTAINED_BY:
+			lower.rngtypid	= rngtypid;
+			lower.inclusive = true;
+			lower.val		= dquery;
+			lower.lower		= true;
+			lower.infinite	= false;
+			upper.rngtypid	= rngtypid;
+			upper.inclusive = true;
+			upper.val		= dquery;
+			upper.lower		= false;
+			upper.infinite	= false;
+			query			= DatumGetRangeType(
+				make_range(fcinfo, &lower, &upper, false));
+			break;
+
+		default:
+			query			= DatumGetRangeType(dquery);
+			break;
+	}
+
+	if (GIST_LEAF(entry))
+		PG_RETURN_BOOL(range_gist_consistent_leaf(
+						   fcinfo, strategy, key, query));
+	else
+		PG_RETURN_BOOL(range_gist_consistent_int(
+						   fcinfo, strategy, key, query));
+}
+
+Datum
+range_gist_union(PG_FUNCTION_ARGS)
+{
+	GistEntryVector		*entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
+	GISTENTRY			*ent	  = entryvec->vector;
+	RangeType			*result_range;
+	int					 i;
+
+	result_range = DatumGetRangeType(ent[0].key);
+
+	for (i = 1; i < entryvec->n; i++)
+	{
+		result_range = range_super_union(fcinfo, result_range,
+										 DatumGetRangeType(ent[i].key));
+	}
+
+	PG_RETURN_RANGE(result_range);
+}
+
+Datum
+range_gist_compress(PG_FUNCTION_ARGS)
+{
+	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+	PG_RETURN_POINTER(entry);
+}
+
+Datum
+range_gist_decompress(PG_FUNCTION_ARGS)
+{
+	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+	PG_RETURN_POINTER(entry);
+}
+
+Datum
+range_gist_penalty(PG_FUNCTION_ARGS)
+{
+	GISTENTRY	*origentry = (GISTENTRY *) PG_GETARG_POINTER(0);
+	GISTENTRY	*newentry  = (GISTENTRY *) PG_GETARG_POINTER(1);
+	float		*penalty   = (float *) PG_GETARG_POINTER(2);
+	RangeType	*orig	   = DatumGetRangeType(origentry->key);
+	RangeType	*new	   = DatumGetRangeType(newentry->key);
+	RangeType	*s_union   = range_super_union(fcinfo, orig, new);
+
+	FmgrInfo	*subtype_diff;
+
+	RangeBound	lower1, lower2;
+	RangeBound	upper1, upper2;
+	bool		empty1, empty2;
+
+	float		lower_diff, upper_diff;
+
+	RangeTypeInfo rngtypinfo;
+
+	range_deserialize(fcinfo, orig, &lower1, &upper1, &empty1);
+	range_deserialize(fcinfo, s_union, &lower2, &upper2, &empty2);
+
+	range_gettypinfo(fcinfo, lower1.rngtypid, &rngtypinfo);
+	subtype_diff = &rngtypinfo.subdiffFn;
+
+	Assert(empty1 || !empty2);
+
+	if (empty1 && empty2)
+		return 0;
+	else if (empty1 && !empty2)
+	{
+		if (lower2.infinite || upper2.infinite)
+			/* from empty to infinite */
+			return get_float8_infinity();
+		else if (subtype_diff->fn_addr != NULL)
+			/* from empty to upper2-lower2 */
+			return DatumGetFloat8(FunctionCall2(subtype_diff,
+												upper2.val, lower2.val));
+		else
+			/* wild guess */
+			return 1.0;
+	}
+
+	Assert(lower2.infinite || !lower1.infinite);
+
+	if (lower2.infinite && !lower1.infinite)
+		lower_diff = get_float8_infinity();
+	else if (lower2.infinite && lower1.infinite)
+		lower_diff = 0;
+	else if (subtype_diff->fn_addr != NULL)
+	{
+		lower_diff = DatumGetFloat8(FunctionCall2(subtype_diff,
+												  lower1.val, lower2.val));
+		if (lower_diff < 0)
+			lower_diff = 0;		/* subtype_diff is broken */
+	}
+	else /* only know whether there is a difference or not */
+		lower_diff = (float) range_cmp_bounds(fcinfo, &lower1, &lower2);
+
+	Assert(upper2.infinite || !upper1.infinite);
+
+	if (upper2.infinite && !upper1.infinite)
+		upper_diff = get_float8_infinity();
+	else if (upper2.infinite && upper1.infinite)
+		upper_diff = 0;
+	else if (subtype_diff->fn_addr != NULL)
+	{
+		upper_diff = DatumGetFloat8(FunctionCall2(subtype_diff,
+												  upper2.val, upper1.val));
+		if (upper_diff < 0)
+			upper_diff = 0;		/* subtype_diff is broken */
+	}
+	else /* only know whether there is a difference or not */
+		upper_diff = (float) range_cmp_bounds(fcinfo, &upper2, &upper1);
+
+	Assert(lower_diff >= 0 && upper_diff >= 0);
+
+	*penalty = (float) (lower_diff + upper_diff);
+	PG_RETURN_POINTER(penalty);
+}
+
+/*
+ * The GiST PickSplit method for ranges
+ *
+ * Algorithm based on sorting. Incoming array of periods is sorted using
+ * sort_item_cmp function. After that first half of periods goes to the left
+ * datum, and the second half of periods goes to the right datum.
+ */
+Datum
+range_gist_picksplit(PG_FUNCTION_ARGS)
+{
+	GistEntryVector		*entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
+	GIST_SPLITVEC		*v		  = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+	OffsetNumber		 i;
+	RangeType			*pred_left;
+	RangeType			*pred_right;
+	PickSplitSortItem	*sortItems;
+	int					 nbytes;
+	OffsetNumber		 split_idx;
+	OffsetNumber		*left;
+	OffsetNumber		*right;
+	OffsetNumber		 maxoff;
+
+	maxoff = entryvec->n - 1;
+	nbytes = (maxoff + 1) * sizeof(OffsetNumber);
+	sortItems = (PickSplitSortItem *) palloc(
+		maxoff * sizeof(PickSplitSortItem));
+	v->spl_left = (OffsetNumber *) palloc(nbytes);
+	v->spl_right = (OffsetNumber *) palloc(nbytes);
+
+	/*
+	 * Preparing auxiliary array and sorting.
+	 */
+	for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
+	{
+		sortItems[i - 1].index	= i;
+		sortItems[i - 1].data	= DatumGetRangeType(entryvec->vector[i].key);
+		sortItems[i - 1].fcinfo = fcinfo;
+	}
+	qsort(sortItems, maxoff, sizeof(PickSplitSortItem), sort_item_cmp);
+	split_idx = maxoff / 2;
+
+	left = v->spl_left;
+	v->spl_nleft = 0;
+	right = v->spl_right;
+	v->spl_nright = 0;
+
+	/*
+	 * First half of segs goes to the left datum.
+	 */
+	pred_left = DatumGetRangeType(sortItems[0].data);
+	*left++ = sortItems[0].index;
+	v->spl_nleft++;
+	for (i = 1; i < split_idx; i++)
+	{
+		pred_left = range_super_union(fcinfo, pred_left,
+									  DatumGetRangeType(sortItems[i].data));
+		*left++ = sortItems[i].index;
+		v->spl_nleft++;
+	}
+
+	/*
+	 * Second half of segs goes to the right datum.
+	 */
+	pred_right = DatumGetRangeType(sortItems[split_idx].data);
+	*right++ = sortItems[split_idx].index;
+	v->spl_nright++;
+	for (i = split_idx + 1; i < maxoff; i++)
+	{
+		pred_right = range_super_union(fcinfo, pred_right,
+									   DatumGetRangeType(sortItems[i].data));
+		*right++ = sortItems[i].index;
+		v->spl_nright++;
+	}
+
+	*left = *right = FirstOffsetNumber; /* sentinel value, see dosplit() */
+
+	v->spl_ldatum = RangeTypeGetDatum(pred_left);
+	v->spl_rdatum = RangeTypeGetDatum(pred_right);
+
+	PG_RETURN_POINTER(v);
+}
+
+Datum
+range_gist_same(PG_FUNCTION_ARGS)
+{
+	Datum r1 = PG_GETARG_DATUM(0);
+	Datum r2 = PG_GETARG_DATUM(1);
+	bool *result = (bool *) PG_GETARG_POINTER(2);
+
+	*result = DatumGetBool(OidFunctionCall2(F_RANGE_EQ, r1, r2));
+	PG_RETURN_POINTER(result);
+}
+
+/*
+ *----------------------------------------------------------
+ * STATIC FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+/* return the smallest range that contains r1 and r2 */
+static RangeType *
+range_super_union(FunctionCallInfo fcinfo, RangeType *r1, RangeType *r2)
+{
+	RangeBound	 lower1, lower2;
+	RangeBound	 upper1, upper2;
+	bool		 empty1, empty2;
+	RangeBound	*result_lower;
+	RangeBound	*result_upper;
+
+	range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
+	range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
+
+	if (empty1)
+		return r2;
+	if (empty2)
+		return r1;
+
+	if (range_cmp_bounds(fcinfo, &lower1, &lower2) <= 0)
+		result_lower = &lower1;
+	else
+		result_lower = &lower2;
+
+	if (range_cmp_bounds(fcinfo, &upper1, &upper2) >= 0)
+		result_upper = &upper1;
+	else
+		result_upper = &upper2;
+
+	/* optimization to avoid constructing a new range */
+	if (result_lower == &lower1 && result_upper == &upper1)
+		return r1;
+	if (result_lower == &lower2 && result_upper == &upper2)
+		return r2;
+
+	return DatumGetRangeType(
+		make_range(fcinfo, result_lower, result_upper, false));
+}
+
+static bool
+range_gist_consistent_int(FunctionCallInfo fcinfo, StrategyNumber strategy,
+						  RangeType *key, RangeType *query)
+{
+	Oid proc = InvalidOid;
+
+	RangeBound	lower1, lower2;
+	RangeBound	upper1, upper2;
+	bool		empty1, empty2;
+
+	bool retval;
+	bool negate = false;
+
+	range_deserialize(fcinfo, key, &lower1, &upper1, &empty1);
+	range_deserialize(fcinfo, query, &lower2, &upper2, &empty2);
+
+	switch (strategy)
+	{
+		case RANGESTRAT_EQ:
+			proc   = F_RANGE_CONTAINS;
+			break;
+		case RANGESTRAT_NE:
+			return true;
+			break;
+		case RANGESTRAT_OVERLAPS:
+			proc   = F_RANGE_OVERLAPS;
+			break;
+		case RANGESTRAT_CONTAINS_ELEM:
+		case RANGESTRAT_CONTAINS:
+			proc   = F_RANGE_CONTAINS;
+			break;
+		case RANGESTRAT_ELEM_CONTAINED_BY:
+		case RANGESTRAT_CONTAINED_BY:
+			return true;
+			break;
+		case RANGESTRAT_BEFORE:
+			if (empty1)
+				return false;
+			proc   = F_RANGE_OVERRIGHT;
+			negate = true;
+			break;
+		case RANGESTRAT_AFTER:
+			if (empty1)
+				return false;
+			proc   = F_RANGE_OVERLEFT;
+			negate = true;
+			break;
+		case RANGESTRAT_OVERLEFT:
+			if (empty1)
+				return false;
+			proc   = F_RANGE_AFTER;
+			negate = true;
+			break;
+		case RANGESTRAT_OVERRIGHT:
+			if (empty1)
+				return false;
+			proc = F_RANGE_BEFORE;
+			negate = true;
+			break;
+		case RANGESTRAT_ADJACENT:
+			if (empty1 || empty2)
+				return false;
+			if (DatumGetBool(
+					OidFunctionCall2(F_RANGE_ADJACENT,
+									 RangeTypeGetDatum(key),
+									 RangeTypeGetDatum(query))))
+				return true;
+			proc = F_RANGE_OVERLAPS;
+			break;
+	}
+
+	retval = DatumGetBool(OidFunctionCall2(proc, RangeTypeGetDatum(key),
+										   RangeTypeGetDatum(query)));
+
+	if (negate)
+		retval = !retval;
+
+	PG_RETURN_BOOL(retval);
+}
+
+static bool
+range_gist_consistent_leaf(FunctionCallInfo fcinfo, StrategyNumber strategy,
+						   RangeType *key, RangeType *query)
+{
+	Oid proc = InvalidOid;
+
+	RangeBound	lower1, lower2;
+	RangeBound	upper1, upper2;
+	bool		empty1, empty2;
+
+	range_deserialize(fcinfo, key, &lower1, &upper1, &empty1);
+	range_deserialize(fcinfo, query, &lower2, &upper2, &empty2);
+
+	switch (strategy)
+	{
+		case RANGESTRAT_EQ:
+			proc = F_RANGE_EQ;
+			break;
+		case RANGESTRAT_NE:
+			proc = F_RANGE_NE;
+			break;
+		case RANGESTRAT_OVERLAPS:
+			proc = F_RANGE_OVERLAPS;
+			break;
+		case RANGESTRAT_CONTAINS_ELEM:
+		case RANGESTRAT_CONTAINS:
+			proc = F_RANGE_CONTAINS;
+			break;
+		case RANGESTRAT_ELEM_CONTAINED_BY:
+		case RANGESTRAT_CONTAINED_BY:
+			proc = F_RANGE_CONTAINED_BY;
+			break;
+		case RANGESTRAT_BEFORE:
+			if (empty1 || empty2)
+				return false;
+			proc = F_RANGE_BEFORE;
+			break;
+		case RANGESTRAT_AFTER:
+			if (empty1 || empty2)
+				return false;
+			proc = F_RANGE_AFTER;
+			break;
+		case RANGESTRAT_OVERLEFT:
+			if (empty1 || empty2)
+				return false;
+			proc = F_RANGE_OVERLEFT;
+			break;
+		case RANGESTRAT_OVERRIGHT:
+			if (empty1 || empty2)
+				return false;
+			proc = F_RANGE_OVERRIGHT;
+			break;
+		case RANGESTRAT_ADJACENT:
+			if (empty1 || empty2)
+				return false;
+			proc = F_RANGE_ADJACENT;
+			break;
+	}
+
+	return DatumGetBool(OidFunctionCall2(proc, RangeTypeGetDatum(key),
+										 RangeTypeGetDatum(query)));
+}
+
+/*
+ * Compare function for PickSplitSortItem. This is actually the
+ * interesting part of the picksplit algorithm.
+ *
+ * We want to separate out empty ranges, bounded ranges, and unbounded
+ * ranges. We assume that "contains" and "overlaps" are the most
+ * important queries, so empty ranges will rarely match and unbounded
+ * ranges frequently will. Bounded ranges should be in the middle.
+ *
+ * Empty ranges we push all the way to the left, then bounded ranges
+ * (sorted on lower bound, then upper), then ranges with no lower
+ * bound, then ranges with no upper bound; and finally, ranges with no
+ * upper or lower bound all the way to the right.
+ */
+static int
+sort_item_cmp(const void *a, const void *b)
+{
+	PickSplitSortItem	*i1 = (PickSplitSortItem *)a;
+	PickSplitSortItem	*i2 = (PickSplitSortItem *)b;
+	RangeType			*r1 = i1->data;
+	RangeType			*r2 = i2->data;
+
+	RangeBound	lower1, lower2;
+	RangeBound	upper1, upper2;
+	bool		empty1, empty2;
+
+	FunctionCallInfo fcinfo = i1->fcinfo;
+
+	int cmp;
+
+	range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
+	range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
+
+	if (empty1 || empty2)
+	{
+		if (empty1 && empty2)
+			return 0;
+		else if (empty1)
+			return -1;
+		else if (empty2)
+			return 1;
+		else
+			Assert(false);
+	}
+
+	/*
+	 * If both lower or both upper bounds are infinite, we sort by
+	 * ascending range size. That means that if both upper bounds are
+	 * infinite, we sort by the lower bound _descending_. That creates
+	 * a slightly odd total order, but keeps the pages with very
+	 * unselective predicates grouped more closely together on the
+	 * right.
+	 */
+	if (lower1.infinite || upper1.infinite ||
+		lower2.infinite || upper2.infinite)
+	{
+		if (lower1.infinite && lower2.infinite)
+			return range_cmp_bounds(fcinfo, &upper1, &upper2);
+		else if (lower1.infinite)
+			return -1;
+		else if (lower2.infinite)
+			return 1;
+		else if (upper1.infinite && upper2.infinite)
+			return -1 * range_cmp_bounds(fcinfo, &lower1, &lower2);
+		else if (upper1.infinite)
+			return 1;
+		else if (upper2.infinite)
+			return -1;
+		else
+			Assert(false);
+	}
+
+	if ((cmp = range_cmp_bounds(fcinfo, &lower1, &lower2)) != 0)
+		return cmp;
+
+	return range_cmp_bounds(fcinfo, &upper1, &upper2);
+}
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 326f1eee92dfc84101dd64364b3fcbb1e623f74c..1b4d26d6593f843d6c7dd2c025ebe2fe3b19df77 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -26,6 +26,7 @@
 #include "catalog/pg_opclass.h"
 #include "catalog/pg_operator.h"
 #include "catalog/pg_proc.h"
+#include "catalog/pg_range.h"
 #include "catalog/pg_statistic.h"
 #include "catalog/pg_type.h"
 #include "miscadmin.h"
@@ -2250,6 +2251,16 @@ type_is_enum(Oid typid)
 	return (get_typtype(typid) == TYPTYPE_ENUM);
 }
 
+/*
+ * type_is_range
+ *	  Returns true if the given type is an range type.
+ */
+bool
+type_is_range(Oid typid)
+{
+	return (get_typtype(typid) == TYPTYPE_RANGE);
+}
+
 /*
  * get_type_category_preferred
  *
@@ -2855,3 +2866,22 @@ get_namespace_name(Oid nspid)
 	else
 		return NULL;
 }
+
+Oid
+get_range_subtype(Oid rangeOid)
+{
+	HeapTuple	tp;
+
+	tp = SearchSysCache1(RANGETYPE, ObjectIdGetDatum(rangeOid));
+	if (HeapTupleIsValid(tp))
+	{
+		Form_pg_range	rngtup = (Form_pg_range) GETSTRUCT(tp);
+		Oid				result;
+
+		result = rngtup->rngsubtype;
+		ReleaseSysCache(tp);
+		return result;
+	}
+	else
+		return InvalidOid;
+}
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 99e5f1d9fe616cbd10458d5457163205b2e97cb6..71b09abb232c93f26201fde1e949e1cfb46a2203 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -43,6 +43,7 @@
 #include "catalog/pg_operator.h"
 #include "catalog/pg_opfamily.h"
 #include "catalog/pg_proc.h"
+#include "catalog/pg_range.h"
 #include "catalog/pg_rewrite.h"
 #include "catalog/pg_statistic.h"
 #include "catalog/pg_tablespace.h"
@@ -554,6 +555,17 @@ static const struct cachedesc cacheinfo[] = {
 		},
 		2048
 	},
+	{RangeRelationId,		/* RANGETYPE */
+		RangeTypidIndexId,
+		1,
+		{
+			Anum_pg_range_rngtypid,
+			0,
+			0,
+			0
+		},
+		1024
+	},
 	{RelationRelationId,		/* RELNAMENSP */
 		ClassNameNspIndexId,
 		2,
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index 0911c8083be36954a7232c8ce4dc73e7190bfb61..3cc2a7ee0758112b43f7919da812eb48f53775ba 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -407,11 +407,13 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
 	int			nargs = declared_args->dim1;
 	bool		have_anyelement_result = false;
 	bool		have_anyarray_result = false;
+	bool		have_anyrange_result = false;
 	bool		have_anynonarray = false;
 	bool		have_anyenum = false;
 	Oid			anyelement_type = InvalidOid;
 	Oid			anyarray_type = InvalidOid;
-	Oid			anycollation;
+	Oid			anyrange_type = InvalidOid;
+	Oid			anycollation = InvalidOid;
 	int			i;
 
 	/* See if there are any polymorphic outputs; quick out if not */
@@ -433,11 +435,15 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
 				have_anyelement_result = true;
 				have_anyenum = true;
 				break;
+			case ANYRANGEOID:
+				have_anyrange_result = true;
+				break;
 			default:
 				break;
 		}
 	}
-	if (!have_anyelement_result && !have_anyarray_result)
+	if (!have_anyelement_result && !have_anyarray_result &&
+		!have_anyrange_result)
 		return true;
 
 	/*
@@ -461,20 +467,47 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
 				if (!OidIsValid(anyarray_type))
 					anyarray_type = get_call_expr_argtype(call_expr, i);
 				break;
+			case ANYRANGEOID:
+				if (!OidIsValid(anyrange_type))
+					anyrange_type = get_call_expr_argtype(call_expr, i);
+				break;
 			default:
 				break;
 		}
 	}
 
 	/* If nothing found, parser messed up */
-	if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type))
+	if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type) &&
+		!OidIsValid(anyrange_type))
+		return false;
+
+	/*
+	 * We can't deduce a range type from the subtype, because there may be
+	 * multiple range types for a single subtype.
+	 */
+	if (have_anyrange_result && !OidIsValid(anyrange_type))
 		return false;
 
 	/* If needed, deduce one polymorphic type from the other */
 	if (have_anyelement_result && !OidIsValid(anyelement_type))
-		anyelement_type = resolve_generic_type(ANYELEMENTOID,
-											   anyarray_type,
-											   ANYARRAYOID);
+	{
+		if (OidIsValid(anyarray_type))
+			anyelement_type = resolve_generic_type(ANYELEMENTOID,
+												   anyarray_type,
+												   ANYARRAYOID);
+		if (OidIsValid(anyrange_type))
+		{
+			Oid subtype = resolve_generic_type(ANYELEMENTOID,
+											   anyrange_type,
+											   ANYRANGEOID);
+			if (OidIsValid(anyelement_type) &&
+				anyelement_type != subtype)
+				return false;
+			else
+				anyelement_type = subtype;
+		}
+	}
+
 	if (have_anyarray_result && !OidIsValid(anyarray_type))
 		anyarray_type = resolve_generic_type(ANYARRAYOID,
 											 anyelement_type,
@@ -492,7 +525,12 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
 	 * Identify the collation to use for polymorphic OUT parameters. (It'll
 	 * necessarily be the same for both anyelement and anyarray.)
 	 */
-	anycollation = get_typcollation(OidIsValid(anyelement_type) ? anyelement_type : anyarray_type);
+
+	if (OidIsValid(anyelement_type))
+		anycollation = get_typcollation(anyelement_type);
+	else if (OidIsValid(anyarray_type))
+		anycollation = get_typcollation(anyarray_type);
+
 	if (OidIsValid(anycollation))
 	{
 		/*
@@ -529,6 +567,14 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
 								   0);
 				TupleDescInitEntryCollation(tupdesc, i + 1, anycollation);
 				break;
+			case ANYRANGEOID:
+				TupleDescInitEntry(tupdesc, i + 1,
+								   NameStr(tupdesc->attrs[i]->attname),
+								   anyrange_type,
+								   -1,
+								   0);
+				TupleDescInitEntryCollation(tupdesc, i + 1, anycollation);
+				break;
 			default:
 				break;
 		}
@@ -552,8 +598,10 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
 {
 	bool		have_anyelement_result = false;
 	bool		have_anyarray_result = false;
+	bool		have_anyrange_result = false;
 	Oid			anyelement_type = InvalidOid;
 	Oid			anyarray_type = InvalidOid;
+	Oid			anyrange_type = InvalidOid;
 	int			inargno;
 	int			i;
 
@@ -597,6 +645,21 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
 					argtypes[i] = anyarray_type;
 				}
 				break;
+			case ANYRANGEOID:
+				if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
+					have_anyrange_result = true;
+				else
+				{
+					if (!OidIsValid(anyrange_type))
+					{
+						anyrange_type = get_call_expr_argtype(call_expr,
+															  inargno);
+						if (!OidIsValid(anyrange_type))
+							return false;
+					}
+					argtypes[i] = anyrange_type;
+				}
+				break;
 			default:
 				break;
 		}
@@ -605,18 +668,42 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
 	}
 
 	/* Done? */
-	if (!have_anyelement_result && !have_anyarray_result)
+	if (!have_anyelement_result && !have_anyarray_result &&
+		!have_anyrange_result)
 		return true;
 
+	/*
+	 * We can't deduce a range type from the subtype, because there may be
+	 * multiple range types for a single subtype.
+	 */
+	if (have_anyrange_result && !OidIsValid(anyrange_type))
+		return false;
+
 	/* If no input polymorphics, parser messed up */
-	if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type))
+	if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type) &&
+		!OidIsValid(anyrange_type))
 		return false;
 
 	/* If needed, deduce one polymorphic type from the other */
 	if (have_anyelement_result && !OidIsValid(anyelement_type))
-		anyelement_type = resolve_generic_type(ANYELEMENTOID,
-											   anyarray_type,
-											   ANYARRAYOID);
+	{
+		if (OidIsValid(anyarray_type))
+			anyelement_type = resolve_generic_type(ANYELEMENTOID,
+												   anyarray_type,
+												   ANYARRAYOID);
+		if (OidIsValid(anyrange_type))
+		{
+			Oid subtype = resolve_generic_type(ANYELEMENTOID,
+											   anyrange_type,
+											   ANYRANGEOID);
+			if (OidIsValid(anyelement_type) &&
+				anyelement_type != subtype)
+				return false;
+			else
+				anyelement_type = subtype;
+		}
+	}
+
 	if (have_anyarray_result && !OidIsValid(anyarray_type))
 		anyarray_type = resolve_generic_type(ANYARRAYOID,
 											 anyelement_type,
@@ -637,6 +724,9 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
 			case ANYARRAYOID:
 				argtypes[i] = anyarray_type;
 				break;
+			case ANYRANGEOID:
+				argtypes[i] = anyrange_type;
+				break;
 			default:
 				break;
 		}
@@ -663,6 +753,7 @@ get_type_func_class(Oid typid)
 		case TYPTYPE_BASE:
 		case TYPTYPE_DOMAIN:
 		case TYPTYPE_ENUM:
+		case TYPTYPE_RANGE:
 			return TYPEFUNC_SCALAR;
 		case TYPTYPE_PSEUDO:
 			if (typid == RECORDOID)
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index c17b52cea8da028985e90b1e0ca8718f33caf993..88a867fe8e33a9bcbb830ff54287d299642cb8bc 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -52,6 +52,7 @@
 #include "catalog/pg_largeobject.h"
 #include "catalog/pg_largeobject_metadata.h"
 #include "catalog/pg_proc.h"
+#include "catalog/pg_range.h"
 #include "catalog/pg_trigger.h"
 #include "catalog/pg_type.h"
 #include "libpq/libpq-fs.h"
@@ -167,6 +168,7 @@ static void dumpExtension(Archive *fout, ExtensionInfo *extinfo);
 static void dumpType(Archive *fout, TypeInfo *tyinfo);
 static void dumpBaseType(Archive *fout, TypeInfo *tyinfo);
 static void dumpEnumType(Archive *fout, TypeInfo *tyinfo);
+static void dumpRangeType(Archive *fout, TypeInfo *tyinfo);
 static void dumpDomain(Archive *fout, TypeInfo *tyinfo);
 static void dumpCompositeType(Archive *fout, TypeInfo *tyinfo);
 static void dumpCompositeTypeColComments(Archive *fout, TypeInfo *tyinfo);
@@ -2989,7 +2991,8 @@ getTypes(int *numTypes)
 		 * should copy the base type's catId, but then it might capture the
 		 * pg_depend entries for the type, which we don't want.
 		 */
-		if (tyinfo[i].dobj.dump && tyinfo[i].typtype == TYPTYPE_BASE)
+		if (tyinfo[i].dobj.dump && (tyinfo[i].typtype == TYPTYPE_BASE ||
+									tyinfo[i].typtype == TYPTYPE_RANGE))
 		{
 			stinfo = (ShellTypeInfo *) malloc(sizeof(ShellTypeInfo));
 			stinfo->dobj.objType = DO_SHELL_TYPE;
@@ -3700,7 +3703,32 @@ getFuncs(int *numFuncs)
 	 * so be sure to fetch any such functions.
 	 */
 
-	if (g_fout->remoteVersion >= 70300)
+	if (g_fout->remoteVersion >= 90200)
+	{
+		appendPQExpBuffer(query,
+						  "SELECT tableoid, oid, proname, prolang, "
+						  "pronargs, proargtypes, prorettype, proacl, "
+						  "pronamespace, "
+						  "(%s proowner) AS rolname "
+						  "FROM pg_proc p "
+						  "WHERE NOT proisagg AND "
+						  " NOT EXISTS (SELECT 1 FROM pg_depend "
+						  "   WHERE classid = 'pg_proc'::regclass AND "
+						  "   objid = p.oid AND deptype = 'i') AND "
+						  "(pronamespace != "
+						  "(SELECT oid FROM pg_namespace "
+						  "WHERE nspname = 'pg_catalog')",
+						  username_subquery);
+		if (binary_upgrade && g_fout->remoteVersion >= 90100)
+			appendPQExpBuffer(query,
+							  " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
+							  "classid = 'pg_proc'::regclass AND "
+							  "objid = p.oid AND "
+							  "refclassid = 'pg_extension'::regclass AND "
+							  "deptype = 'e')");
+		appendPQExpBuffer(query, ")");
+	}
+	else if (g_fout->remoteVersion >= 70300)
 	{
 		appendPQExpBuffer(query,
 						  "SELECT tableoid, oid, proname, prolang, "
@@ -7309,6 +7337,8 @@ dumpType(Archive *fout, TypeInfo *tyinfo)
 		dumpCompositeType(fout, tyinfo);
 	else if (tyinfo->typtype == TYPTYPE_ENUM)
 		dumpEnumType(fout, tyinfo);
+	else if (tyinfo->typtype == TYPTYPE_RANGE)
+		dumpRangeType(fout, tyinfo);
 }
 
 /*
@@ -7432,6 +7462,156 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo)
 	destroyPQExpBuffer(query);
 }
 
+/*
+ * dumpRangeType
+ *	  writes out to fout the queries to recreate a user-defined range type
+ */
+static void
+dumpRangeType(Archive *fout, TypeInfo *tyinfo)
+{
+	PQExpBuffer q = createPQExpBuffer();
+	PQExpBuffer delq = createPQExpBuffer();
+	PQExpBuffer labelq = createPQExpBuffer();
+	PQExpBuffer query = createPQExpBuffer();
+	PGresult   *res;
+
+	Oid			 collationOid;
+	Oid			 opclassOid;
+	Oid			 analyzeOid;
+	Oid			 canonicalOid;
+	Oid			 subdiffOid;
+
+	/* Set proper schema search path */
+	selectSourceSchema("pg_catalog");
+
+	appendPQExpBuffer(query,
+					  "SELECT rngtypid, "
+					  "format_type(rngsubtype, NULL) as rngsubtype, "
+					  "rngsubtype::oid as rngsubtypeoid, "
+					  "opc.opcname AS opcname, "
+					  "CASE WHEN rngcollation = st.typcollation THEN 0 "
+					  "     ELSE rngcollation END AS collation, "
+					  "CASE WHEN opcdefault THEN 0 ELSE rngsubopc END "
+					  "  AS rngsubopc, "
+					  "(SELECT nspname FROM pg_namespace nsp "
+					  "  WHERE nsp.oid = opc.opcnamespace) AS opcnsp, "
+					  "t.typanalyze, t.typanalyze::oid as typanalyzeoid, "
+					  "rngcanonical, rngcanonical::oid as rngcanonicaloid, "
+					  "rngsubdiff, rngsubdiff::oid as rngsubdiffoid "
+					  "FROM pg_catalog.pg_type t, pg_type st, "
+					  "     pg_catalog.pg_opclass opc, pg_catalog.pg_range r "
+					  "WHERE t.oid = rngtypid AND st.oid = rngsubtype AND "
+					  "      opc.oid = rngsubopc AND rngtypid = '%u'",
+					  tyinfo->dobj.catId.oid);
+
+	res = PQexec(g_conn, query->data);
+	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
+
+	/*
+	 * DROP must be fully qualified in case same name appears in pg_catalog.
+	 * CASCADE shouldn't be required here as for normal types since the I/O
+	 * functions are generic and do not get dropped.
+	 */
+	appendPQExpBuffer(delq, "DROP TYPE %s.",
+					  fmtId(tyinfo->dobj.namespace->dobj.name));
+	appendPQExpBuffer(delq, "%s;\n",
+					  fmtId(tyinfo->dobj.name));
+
+	/* We might already have a shell type, but setting pg_type_oid is harmless */
+	if (binary_upgrade)
+		binary_upgrade_set_type_oids_by_type_oid(q, tyinfo->dobj.catId.oid);
+
+	appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (",
+					  fmtId(tyinfo->dobj.name));
+
+	/* SUBTYPE */
+	appendPQExpBuffer(q, "\n    SUBTYPE = %s",
+					  PQgetvalue(res, 0, PQfnumber(res, "rngsubtype")));
+
+	/* COLLATION */
+	collationOid = atooid(PQgetvalue(res, 0,
+									 PQfnumber(res, "collation")));
+	if (OidIsValid(collationOid))
+	{
+		CollInfo   *coll;
+
+		coll = findCollationByOid(collationOid);
+		if (coll)
+		{
+			/* always schema-qualify, don't try to be smart */
+			appendPQExpBuffer(q, ",\n    COLLATION = %s.",
+							  fmtId(coll->dobj.namespace->dobj.name));
+			appendPQExpBuffer(q, "%s",
+							  fmtId(coll->dobj.name));
+		}
+	}
+
+	/* SUBTYPE_OPCLASS */
+	opclassOid = atooid(PQgetvalue(res, 0,
+								   PQfnumber(res, "rngsubopc")));
+	if (OidIsValid(opclassOid))
+	{
+		char *opcname = PQgetvalue(res, 0, PQfnumber(res, "opcname"));
+		char *nspname = PQgetvalue(res, 0, PQfnumber(res, "opcnsp"));
+
+		/* always schema-qualify, don't try to be smart */
+		appendPQExpBuffer(q, ",\n    SUBTYPE_OPCLASS = %s.",
+						  fmtId(nspname));
+		appendPQExpBuffer(q, "%s", fmtId(opcname));
+	}
+
+	/* ANALYZE */
+	analyzeOid = atooid(PQgetvalue(res, 0,
+								   PQfnumber(res, "typanalyzeoid")));
+	if (OidIsValid(analyzeOid))
+		appendPQExpBuffer(q, ",\n    ANALYZE = %s",
+						  PQgetvalue(res, 0, PQfnumber(res, "typanalyze")));
+
+	/* CANONICAL */
+	canonicalOid = atooid(PQgetvalue(res, 0,
+									 PQfnumber(res, "rngcanonicaloid")));
+	if (OidIsValid(canonicalOid))
+		appendPQExpBuffer(q, ",\n    CANONICAL = %s",
+						  PQgetvalue(res, 0, PQfnumber(res, "rngcanonical")));
+
+	/* SUBTYPE_DIFF */
+	subdiffOid = atooid(PQgetvalue(res, 0, PQfnumber(res, "rngsubdiffoid")));
+	if (OidIsValid(subdiffOid))
+		appendPQExpBuffer(q, ",\n    SUBTYPE_DIFF = %s",
+						  PQgetvalue(res, 0, PQfnumber(res, "rngsubdiff")));
+
+	appendPQExpBuffer(q, "\n);\n");
+
+	appendPQExpBuffer(labelq, "TYPE %s", fmtId(tyinfo->dobj.name));
+
+	if (binary_upgrade)
+		binary_upgrade_extension_member(q, &tyinfo->dobj, labelq->data);
+
+	ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
+				 tyinfo->dobj.name,
+				 tyinfo->dobj.namespace->dobj.name,
+				 NULL,
+				 tyinfo->rolname, false,
+				 "TYPE", SECTION_PRE_DATA,
+				 q->data, delq->data, NULL,
+				 tyinfo->dobj.dependencies, tyinfo->dobj.nDeps,
+				 NULL, NULL);
+
+	/* Dump Type Comments and Security Labels */
+	dumpComment(fout, labelq->data,
+				tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
+				tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
+	dumpSecLabel(fout, labelq->data,
+				 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
+				 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
+
+	PQclear(res);
+	destroyPQExpBuffer(q);
+	destroyPQExpBuffer(delq);
+	destroyPQExpBuffer(labelq);
+	destroyPQExpBuffer(query);
+}
+
 /*
  * dumpBaseType
  *	  writes out to fout the queries to recreate a user-defined base type
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index c6273c12671d7e37874a301f7b29cf7d762ebe0f..0a0ebcf91666199bd89f21c09b3ae97570743d6e 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,7 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	201110221
+/* COMMITTER: please set appropriately */
+#define CATALOG_VERSION_NO	201111111
 
 #endif
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 9a8e6ffc8a549ed405bea969f7e08f3984f0f24d..0bf2d4d640957957ebf5e5720601da189f36d21d 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -303,6 +303,9 @@ DECLARE_UNIQUE_INDEX(pg_extension_oid_index, 3080, on pg_extension using btree(o
 DECLARE_UNIQUE_INDEX(pg_extension_name_index, 3081, on pg_extension using btree(extname name_ops));
 #define ExtensionNameIndexId 3081
 
+DECLARE_UNIQUE_INDEX(pg_range_rgntypid_index, 3542, on pg_range using btree(rngtypid oid_ops));
+#define RangeTypidIndexId					3542
+
 /* last step of initialization script: build the indexes declared above */
 BUILD_INDICES
 
diff --git a/src/include/catalog/pg_amop.h b/src/include/catalog/pg_amop.h
index 3b88c41599e77c690e27c7f7344d6c8cebb29876..ede0c2ddf64d14783d04cd81a501661bc647955c 100644
--- a/src/include/catalog/pg_amop.h
+++ b/src/include/catalog/pg_amop.h
@@ -709,4 +709,34 @@ DATA(insert (	3683   3615 3615 5 s	3679 403 0 ));
 DATA(insert (	3702   3615 3615 7 s	3693 783 0 ));
 DATA(insert (	3702   3615 3615 8 s	3694 783 0 ));
 
+/*
+ * btree range_ops
+ */
+DATA(insert (	3901   3831 3831 1 s	3884 403 0 ));
+DATA(insert (	3901   3831 3831 2 s	3885 403 0 ));
+DATA(insert (	3901   3831 3831 3 s	3882 403 0 ));
+DATA(insert (	3901   3831 3831 4 s	3886 403 0 ));
+DATA(insert (	3901   3831 3831 5 s	3887 403 0 ));
+
+/*
+ * hash range_ops
+ */
+DATA(insert (	3903   3831 3831 1 s	3882 405 0 ));
+
+/*
+ * GiST range_ops
+ */
+DATA(insert (	3919   3831 3831 1 s	3882 783 0 ));
+DATA(insert (	3919   3831 3831 2 s	3883 783 0 ));
+DATA(insert (	3919   3831 3831 3 s	3888 783 0 ));
+DATA(insert (	3919   3831 2776 4 s	3889 783 0 ));
+DATA(insert (	3919   2776 3831 5 s	3891 783 0 ));
+DATA(insert (	3919   3831 3831 6 s	3890 783 0 ));
+DATA(insert (	3919   3831 3831 7 s	3892 783 0 ));
+DATA(insert (	3919   3831 3831 8 s	3893 783 0 ));
+DATA(insert (	3919   3831 3831 9 s	3894 783 0 ));
+DATA(insert (	3919   3831 3831 10 s	3895 783 0 ));
+DATA(insert (	3919   3831 3831 11 s	3896 783 0 ));
+DATA(insert (	3919   3831 3831 12 s	3897 783 0 ));
+
 #endif   /* PG_AMOP_H */
diff --git a/src/include/catalog/pg_amproc.h b/src/include/catalog/pg_amproc.h
index 9e2da2c30b2afe368408cec24588f909ccad42a2..e5d43f7c1d3ef02322ad2c5b376885a17e5f7430 100644
--- a/src/include/catalog/pg_amproc.h
+++ b/src/include/catalog/pg_amproc.h
@@ -336,5 +336,14 @@ DATA(insert (	3659   3614 3614 4 3658 ));
 DATA(insert (	3659   3614 3614 5 2700 ));
 DATA(insert (	3626   3614 3614 1 3622 ));
 DATA(insert (	3683   3615 3615 1 3668 ));
+DATA(insert (	3901   3831 3831 1 3870 ));
+DATA(insert (	3903   3831 3831 1 3902 ));
+DATA(insert (	3919   3831 3831 1 3875 ));
+DATA(insert (	3919   3831 3831 2 3876 ));
+DATA(insert (	3919   3831 3831 3 3877 ));
+DATA(insert (	3919   3831 3831 4 3878 ));
+DATA(insert (	3919   3831 3831 5 3879 ));
+DATA(insert (	3919   3831 3831 6 3880 ));
+DATA(insert (	3919   3831 3831 7 3881 ));
 
 #endif   /* PG_AMPROC_H */
diff --git a/src/include/catalog/pg_opclass.h b/src/include/catalog/pg_opclass.h
index d723b2561ae92796e8d67d338a37fdedc99793c1..05ffa0384cff89d6e6475a2175f8504d4b4869fa 100644
--- a/src/include/catalog/pg_opclass.h
+++ b/src/include/catalog/pg_opclass.h
@@ -213,5 +213,8 @@ DATA(insert (	783		tsvector_ops		PGNSP PGUID 3655  3614 t 3642 ));
 DATA(insert (	2742	tsvector_ops		PGNSP PGUID 3659  3614 t 25 ));
 DATA(insert (	403		tsquery_ops			PGNSP PGUID 3683  3615 t 0 ));
 DATA(insert (	783		tsquery_ops			PGNSP PGUID 3702  3615 t 20 ));
+DATA(insert (	403		range_ops			PGNSP PGUID 3901  3831 t 0 ));
+DATA(insert (	405		range_ops			PGNSP PGUID 3903  3831 t 0 ));
+DATA(insert (	783		range_ops			PGNSP PGUID 3919  3831 t 0 ));
 
 #endif   /* PG_OPCLASS_H */
diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h
index 64f1391b00059d82228c779ae31e3fd1a197c984..f587f5b198df3741a3ea4e2a18d907a947a90fff 100644
--- a/src/include/catalog/pg_operator.h
+++ b/src/include/catalog/pg_operator.h
@@ -1661,6 +1661,45 @@ DESCR("less than or equal");
 DATA(insert OID = 2993 (  ">="	   PGNSP PGUID b f f 2249 2249 16 2992 2990 record_ge scalargtsel scalargtjoinsel ));
 DESCR("greater than or equal");
 
+/* generic range type operators */
+DATA(insert OID = 3882 (  "="	   PGNSP PGUID b t t 3831 3831 16 3882 3883 range_eq eqsel eqjoinsel ));
+DESCR("equal");
+DATA(insert OID = 3883 (  "<>"	   PGNSP PGUID b f f 3831 3831 16 3883 3882 range_ne neqsel neqjoinsel ));
+DESCR("not equal");
+DATA(insert OID = 3884 (  "<"	   PGNSP PGUID b f f 3831 3831 16 3887 3886 range_lt scalarltsel scalarltjoinsel ));
+DESCR("less than");
+DATA(insert OID = 3885 (  "<="	   PGNSP PGUID b f f 3831 3831 16 3886 3887 range_le scalarltsel scalarltjoinsel ));
+DESCR("less than or equal");
+DATA(insert OID = 3886 (  ">="	   PGNSP PGUID b f f 3831 3831 16 3885 3884 range_ge scalargtsel scalargtjoinsel ));
+DESCR("greater than or equal");
+DATA(insert OID = 3887 (  ">"	   PGNSP PGUID b f f 3831 3831 16 3884 3885 range_gt scalargtsel scalargtjoinsel ));
+DESCR("greater than");
+DATA(insert OID = 3888 (  "&&"	   PGNSP PGUID b f f 3831 3831 16 3888 0 3857 - - ));
+DESCR("overlaps");
+DATA(insert OID = 3889 (  "@>"	   PGNSP PGUID b f f 3831 2776 16 3891 0 3858 - - ));
+DESCR("contains");
+DATA(insert OID = 3890 (  "@>"	   PGNSP PGUID b f f 3831 3831 16 3892 0 3859 - - ));
+DESCR("contains");
+DATA(insert OID = 3891 (  "<@"	   PGNSP PGUID b f f 2776 3831 16 3889 0 3860 - - ));
+DESCR("contained by");
+DATA(insert OID = 3892 (  "<@"	   PGNSP PGUID b f f 3831 3831 16 3890 0 3861 - - ));
+DESCR("contained by");
+DATA(insert OID = 3893 (  "<<"	   PGNSP PGUID b f f 3831 3831 16 0 0 before scalarltsel scalarltjoinsel ));
+DESCR("left of");
+DATA(insert OID = 3894 (  ">>"	   PGNSP PGUID b f f 3831 3831 16 0 0 after scalargtsel scalargtjoinsel ));
+DESCR("right of");
+DATA(insert OID = 3895 (  "&<"	   PGNSP PGUID b f f 3831 3831 16 0 0 overleft scalarltsel scalarltjoinsel ));
+DESCR("overlaps to left");
+DATA(insert OID = 3896 (  "&>"	   PGNSP PGUID b f f 3831 3831 16 0 0 overright scalargtsel scalargtjoinsel ));
+DESCR("overlaps to right");
+DATA(insert OID = 3897 (  "-|-"	   PGNSP PGUID b f f 3831 3831 16 3897 0 adjacent - - ));
+DESCR("adjacent");
+DATA(insert OID = 3898 (  "+"	   PGNSP PGUID b f f 3831 3831 3831 3898 0 range_union - - ));
+DESCR("range union");
+DATA(insert OID = 3899 (  "-"	   PGNSP PGUID b f f 3831 3831 3831 0 0 minus - - ));
+DESCR("range difference");
+DATA(insert OID = 3900 (  "*"	   PGNSP PGUID b f f 3831 3831 3831 3900 0 range_intersect - - ));
+DESCR("intersection");
 
 /*
  * function prototypes
diff --git a/src/include/catalog/pg_opfamily.h b/src/include/catalog/pg_opfamily.h
index 548727dbd2474236cc6b0d6ed483eaafd18c130f..5ea949bec6b006ffe67d38539bb06abf6a807c41 100644
--- a/src/include/catalog/pg_opfamily.h
+++ b/src/include/catalog/pg_opfamily.h
@@ -139,5 +139,8 @@ DATA(insert OID = 3655 (	783		tsvector_ops	PGNSP PGUID ));
 DATA(insert OID = 3659 (	2742	tsvector_ops	PGNSP PGUID ));
 DATA(insert OID = 3683 (	403		tsquery_ops		PGNSP PGUID ));
 DATA(insert OID = 3702 (	783		tsquery_ops		PGNSP PGUID ));
+DATA(insert OID = 3901 (	403		range_ops		PGNSP PGUID ));
+DATA(insert OID = 3903 (	405		range_ops		PGNSP PGUID ));
+DATA(insert OID = 3919 (	783		range_ops		PGNSP PGUID ));
 
 #endif   /* PG_OPFAMILY_H */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 64b7a6a314d1084665caa5923fda8629ebd2f81b..3b654ff7c45ad2d7defd069364bb7a9d500e9849 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -4334,6 +4334,153 @@ DESCR("fetch the last row value");
 DATA(insert OID = 3114 (  nth_value		PGNSP PGUID 12 1 0 0 0 f t f t f i 2 0 2283 "2283 23" _null_ _null_ _null_ _null_ window_nth_value _null_ _null_ _null_ ));
 DESCR("fetch the Nth row value");
 
+/* procs for range types */
+DATA(insert OID = 3832 (  anyrange_in	PGNSP PGUID 12 1 0 0 0 f f f t f s 3 0 3831 "2275 26 23" _null_ _null_ _null_ _null_ anyrange_in _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID = 3833 (  anyrange_out	PGNSP PGUID 12 1 0 0 0 f f f t f s 1 0 2275 "3831" _null_ _null_ _null_ _null_ anyrange_out _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID = 3834 (  range_in	PGNSP PGUID 12 1 0 0 0 f f f t f s 3 0 3831 "2275 26 23" _null_ _null_ _null_ _null_ range_in _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID = 3835 (  range_out	PGNSP PGUID 12 1 0 0 0 f f f t f s 1 0 2275 "3831" _null_ _null_ _null_ _null_ range_out _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID = 3836 (  range_recv	PGNSP PGUID 12 1 0 0 0 f f f t f s 3 0 3831 "2281 26 23" _null_ _null_ _null_ _null_ range_recv _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID = 3837 (  range_send	PGNSP PGUID 12 1 0 0 0 f f f t f s 1 0 17 "3831" _null_ _null_ _null_ _null_ range_send _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID = 3848 (  lower	PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 2283 "3831" _null_ _null_ _null_ _null_ range_lower _null_ _null_ _null_ ));
+DESCR("return the range's lower bound");
+DATA(insert OID = 3849 (  upper	PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 2283 "3831" _null_ _null_ _null_ _null_ range_upper _null_ _null_ _null_ ));
+DESCR("return the range's upper bound");
+DATA(insert OID = 3850 (  isempty	PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 16 "3831" _null_ _null_ _null_ _null_ range_empty _null_ _null_ _null_ ));
+DESCR("is the range empty?");
+DATA(insert OID = 3851 (  lower_inc	PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 16 "3831" _null_ _null_ _null_ _null_ range_lower_inc _null_ _null_ _null_ ));
+DESCR("is the range's lower bound inclusive?");
+DATA(insert OID = 3852 (  upper_inc	PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 16 "3831" _null_ _null_ _null_ _null_ range_upper_inc _null_ _null_ _null_ ));
+DESCR("is the range's upper bound inclusive?");
+DATA(insert OID = 3853 (  lower_inf	PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 16 "3831" _null_ _null_ _null_ _null_ range_lower_inf _null_ _null_ _null_ ));
+DESCR("is the range's lower bound infinite?");
+DATA(insert OID = 3854 (  upper_inf	PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 16 "3831" _null_ _null_ _null_ _null_ range_upper_inf _null_ _null_ _null_ ));
+DESCR("is the range's upper bound infinite?");
+DATA(insert OID = 3855 (  range_eq	PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_eq _null_ _null_ _null_ ));
+DESCR("implementation of = operator");
+DATA(insert OID = 3856 (  range_ne	PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_ne _null_ _null_ _null_ ));
+DESCR("implementation of <> operator");
+DATA(insert OID = 3857 (  overlaps	PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_overlaps _null_ _null_ _null_ ));
+DESCR("implementation of && operator");
+DATA(insert OID = 3858 (  contains	PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "3831 2776" _null_ _null_ _null_ _null_ range_contains_elem _null_ _null_ _null_ ));
+DESCR("implementation of @> operator");
+DATA(insert OID = 3859 (  contains	PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_contains _null_ _null_ _null_ ));
+DESCR("implementation of @> operator");
+DATA(insert OID = 3860 (  contained_by	PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "2776 3831" _null_ _null_ _null_ _null_ elem_contained_by_range _null_ _null_ _null_ ));
+DESCR("implementation of <@ operator");
+DATA(insert OID = 3861 (  contained_by	PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_contained_by _null_ _null_ _null_ ));
+DESCR("implementation of <@ operator");
+DATA(insert OID = 3862 (  adjacent	PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_adjacent _null_ _null_ _null_ ));
+DESCR("implementation of -|- operator");
+DATA(insert OID = 3863 (  before	PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_before _null_ _null_ _null_ ));
+DESCR("implementation of << operator");
+DATA(insert OID = 3864 (  after	PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_after _null_ _null_ _null_ ));
+DESCR("implementation of >> operator");
+DATA(insert OID = 3865 (  overleft	PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_overleft _null_ _null_ _null_ ));
+DESCR("implementation of &< operator");
+DATA(insert OID = 3866 (  overright	PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_overright _null_ _null_ _null_ ));
+DESCR("implementation of &> operator");
+DATA(insert OID = 3867 (  range_union	PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 3831 "3831 3831" _null_ _null_ _null_ _null_ range_union _null_ _null_ _null_ ));
+DESCR("implementation of + operator");
+DATA(insert OID = 3868 (  range_intersect	PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 3831 "3831 3831" _null_ _null_ _null_ _null_ range_intersect _null_ _null_ _null_ ));
+DESCR("implementation of * operator");
+DATA(insert OID = 3869 (  minus	PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 3831 "3831 3831" _null_ _null_ _null_ _null_ range_minus _null_ _null_ _null_ ));
+DESCR("implementation of - operator");
+DATA(insert OID = 3870 (  range_cmp	PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 23 "3831 3831" _null_ _null_ _null_ _null_ range_cmp _null_ _null_ _null_ ));
+DESCR("less-equal-greater");
+DATA(insert OID = 3871 (  range_lt	PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_lt _null_ _null_ _null_ ));
+DATA(insert OID = 3872 (  range_le	PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_le _null_ _null_ _null_ ));
+DATA(insert OID = 3873 (  range_ge	PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_ge _null_ _null_ _null_ ));
+DATA(insert OID = 3874 (  range_gt	PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_gt _null_ _null_ _null_ ));
+DATA(insert OID = 3875 (  range_gist_consistent	PGNSP PGUID 12 1 0 0 0 f f f t f i 5 0 16 "2281 3831 21 26 2281" _null_ _null_ _null_ _null_ range_gist_consistent _null_ _null_ _null_ ));
+DESCR("GiST support");
+DATA(insert OID = 3876 (  range_gist_union	PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ range_gist_union _null_ _null_ _null_ ));
+DESCR("GiST support");
+DATA(insert OID = 3877 (  range_gist_compress	PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 2281 "2281" _null_ _null_ _null_ _null_ range_gist_compress _null_ _null_ _null_ ));
+DESCR("GiST support");
+DATA(insert OID = 3878 (  range_gist_decompress	PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 2281 "2281" _null_ _null_ _null_ _null_ range_gist_decompress _null_ _null_ _null_ ));
+DESCR("GiST support");
+DATA(insert OID = 3879 (  range_gist_penalty	PGNSP PGUID 12 1 0 0 0 f f f t f i 3 0 2281 "2281 2281 2281" _null_ _null_ _null_ _null_ range_gist_penalty _null_ _null_ _null_ ));
+DESCR("GiST support");
+DATA(insert OID = 3880 (  range_gist_picksplit	PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ range_gist_picksplit _null_ _null_ _null_ ));
+DESCR("GiST support");
+DATA(insert OID = 3881 (  range_gist_same	PGNSP PGUID 12 1 0 0 0 f f f t f i 3 0 2281 "2281 2281 2281" _null_ _null_ _null_ _null_ range_gist_same _null_ _null_ _null_ ));
+DESCR("GiST support");
+DATA(insert OID = 3902 (  hash_range		   PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 23 "3831" _null_ _null_ _null_ _null_ hash_range _null_ _null_ _null_ ));
+DESCR("hash a range");
+DATA(insert OID = 3914 (  int4range_canonical		   PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 3904 "3904" _null_ _null_ _null_ _null_ int4range_canonical _null_ _null_ _null_ ));
+DESCR("convert an int4 range to canonical form");
+DATA(insert OID = 3928 (  int8range_canonical		   PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 3926 "3926" _null_ _null_ _null_ _null_ int8range_canonical _null_ _null_ _null_ ));
+DESCR("convert an int8 range to canonical form");
+DATA(insert OID = 3915 (  daterange_canonical		   PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 3906 "3906" _null_ _null_ _null_ _null_ daterange_canonical _null_ _null_ _null_ ));
+DESCR("convert a date range to canonical form");
+DATA(insert OID = 3922 (  int4range_subdiff		   PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 701 "23 23" _null_ _null_ _null_ _null_ int4range_subdiff _null_ _null_ _null_ ));
+DESCR("float8 difference of two int4 values");
+DATA(insert OID = 3923 (  int8range_subdiff		   PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 701 "20 20" _null_ _null_ _null_ _null_ int8range_subdiff _null_ _null_ _null_ ));
+DESCR("float8 difference of two int8 values");
+DATA(insert OID = 3924 (  numrange_subdiff		   PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 701 "1700 1700" _null_ _null_ _null_ _null_ numrange_subdiff _null_ _null_ _null_ ));
+DESCR("float8 difference of two numeric values");
+DATA(insert OID = 3925 (  daterange_subdiff		   PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 701 "1082 1082" _null_ _null_ _null_ _null_ daterange_subdiff _null_ _null_ _null_ ));
+DESCR("float8 difference of two date values");
+DATA(insert OID = 3929 (  tsrange_subdiff		   PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 701 "1114 1114" _null_ _null_ _null_ _null_ tsrange_subdiff _null_ _null_ _null_ ));
+DESCR("float8 difference of two timestamp values");
+DATA(insert OID = 3930 (  tstzrange_subdiff		   PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 701 "1184 1184" _null_ _null_ _null_ _null_ tstzrange_subdiff _null_ _null_ _null_ ));
+DESCR("float8 difference of two timestamp with time zone values");
+
+
+DATA(insert OID = 3838 (  int4range	PGNSP PGUID 12 1 0 0 0 f f f f f i 0 0 3904 "" _null_ _null_ _null_ _null_ range_constructor0 _null_ _null_ _null_ ));
+DESCR("int4range constructor");
+DATA(insert OID = 3839 (  int4range	PGNSP PGUID 12 1 0 0 0 f f f f f i 1 0 3904 "23" _null_ _null_ _null_ _null_ range_constructor1 _null_ _null_ _null_ ));
+DESCR("int4range constructor");
+DATA(insert OID = 3840 (  int4range	PGNSP PGUID 12 1 0 0 0 f f f f f i 2 0 3904 "23 23" _null_ _null_ _null_ _null_ range_constructor2 _null_ _null_ _null_ ));
+DESCR("int4range constructor");
+DATA(insert OID = 3841 (  int4range	PGNSP PGUID 12 1 0 0 0 f f f f f i 3 0 3904 "23 23 25" _null_ _null_ _null_ _null_ range_constructor3 _null_ _null_ _null_ ));
+DESCR("int4range constructor");
+DATA(insert OID = 3842 (  numrange	PGNSP PGUID 12 1 0 0 0 f f f f f i 0 0 3906 "" _null_ _null_ _null_ _null_ range_constructor0 _null_ _null_ _null_ ));
+DESCR("numrange constructor");
+DATA(insert OID = 3843 (  numrange	PGNSP PGUID 12 1 0 0 0 f f f f f i 1 0 3906 "1700" _null_ _null_ _null_ _null_ range_constructor1 _null_ _null_ _null_ ));
+DESCR("numrange constructor");
+DATA(insert OID = 3844 (  numrange	PGNSP PGUID 12 1 0 0 0 f f f f f i 2 0 3906 "1700 1700" _null_ _null_ _null_ _null_ range_constructor2 _null_ _null_ _null_ ));
+DESCR("numrange constructor");
+DATA(insert OID = 3845 (  numrange	PGNSP PGUID 12 1 0 0 0 f f f f f i 3 0 3906 "1700 1700 25" _null_ _null_ _null_ _null_ range_constructor3 _null_ _null_ _null_ ));
+DESCR("numrange constructor");
+DATA(insert OID = 3846 (  tsrange	PGNSP PGUID 12 1 0 0 0 f f f f f i 0 0 3908 "" _null_ _null_ _null_ _null_ range_constructor0 _null_ _null_ _null_ ));
+DESCR("tsrange constructor");
+DATA(insert OID = 3847 (  tsrange	PGNSP PGUID 12 1 0 0 0 f f f f f i 1 0 3908 "1114" _null_ _null_ _null_ _null_ range_constructor1 _null_ _null_ _null_ ));
+DESCR("tsrange constructor");
+DATA(insert OID = 3933 (  tsrange	PGNSP PGUID 12 1 0 0 0 f f f f f i 2 0 3908 "1114 1114" _null_ _null_ _null_ _null_ range_constructor2 _null_ _null_ _null_ ));
+DESCR("tsrange constructor");
+DATA(insert OID = 3934 (  tsrange	PGNSP PGUID 12 1 0 0 0 f f f f f i 3 0 3908 "1114 1114 25" _null_ _null_ _null_ _null_ range_constructor3 _null_ _null_ _null_ ));
+DESCR("tsrange constructor");
+DATA(insert OID = 3935 (  tstzrange	PGNSP PGUID 12 1 0 0 0 f f f f f i 0 0 3910 "" _null_ _null_ _null_ _null_ range_constructor0 _null_ _null_ _null_ ));
+DESCR("tstzrange constructor");
+DATA(insert OID = 3936 (  tstzrange	PGNSP PGUID 12 1 0 0 0 f f f f f i 1 0 3910 "1184" _null_ _null_ _null_ _null_ range_constructor1 _null_ _null_ _null_ ));
+DESCR("tstzrange constructor");
+DATA(insert OID = 3937 (  tstzrange	PGNSP PGUID 12 1 0 0 0 f f f f f i 2 0 3910 "1184 1184" _null_ _null_ _null_ _null_ range_constructor2 _null_ _null_ _null_ ));
+DESCR("tstzrange constructor");
+DATA(insert OID = 3938 (  tstzrange	PGNSP PGUID 12 1 0 0 0 f f f f f i 3 0 3910 "1184 1184 25" _null_ _null_ _null_ _null_ range_constructor3 _null_ _null_ _null_ ));
+DESCR("tstzrange constructor");
+DATA(insert OID = 3939 (  daterange	PGNSP PGUID 12 1 0 0 0 f f f f f i 0 0 3912 "" _null_ _null_ _null_ _null_ range_constructor0 _null_ _null_ _null_ ));
+DESCR("daterange constructor");
+DATA(insert OID = 3940 (  daterange	PGNSP PGUID 12 1 0 0 0 f f f f f i 1 0 3912 "1082" _null_ _null_ _null_ _null_ range_constructor1 _null_ _null_ _null_ ));
+DESCR("daterange constructor");
+DATA(insert OID = 3941 (  daterange	PGNSP PGUID 12 1 0 0 0 f f f f f i 2 0 3912 "1082 1082" _null_ _null_ _null_ _null_ range_constructor2 _null_ _null_ _null_ ));
+DESCR("daterange constructor");
+DATA(insert OID = 3942 (  daterange	PGNSP PGUID 12 1 0 0 0 f f f f f i 3 0 3912 "1082 1082 25" _null_ _null_ _null_ _null_ range_constructor3 _null_ _null_ _null_ ));
+DESCR("daterange constructor");
+DATA(insert OID = 3943 (  int8range	PGNSP PGUID 12 1 0 0 0 f f f f f i 0 0 3926 "" _null_ _null_ _null_ _null_ range_constructor0 _null_ _null_ _null_ ));
+DESCR("int8range constructor");
+DATA(insert OID = 3944 (  int8range	PGNSP PGUID 12 1 0 0 0 f f f f f i 1 0 3926 "20" _null_ _null_ _null_ _null_ range_constructor1 _null_ _null_ _null_ ));
+DESCR("int8range constructor");
+DATA(insert OID = 3945 (  int8range	PGNSP PGUID 12 1 0 0 0 f f f f f i 2 0 3926 "20 20" _null_ _null_ _null_ _null_ range_constructor2 _null_ _null_ _null_ ));
+DESCR("int8range constructor");
+DATA(insert OID = 3946 (  int8range	PGNSP PGUID 12 1 0 0 0 f f f f f i 3 0 3926 "20 20 25" _null_ _null_ _null_ _null_ range_constructor3 _null_ _null_ _null_ ));
+DESCR("int8range constructor");
 
 /*
  * Symbolic values for provolatile column: these indicate whether the result
diff --git a/src/include/catalog/pg_range.h b/src/include/catalog/pg_range.h
new file mode 100644
index 0000000000000000000000000000000000000000..19b437db8dc27af3c6b12915f06c519dfb8c07fa
--- /dev/null
+++ b/src/include/catalog/pg_range.h
@@ -0,0 +1,84 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_range.h
+ *	  definition of the system "range" relation (pg_range)
+ *	  along with the relation's initial contents.
+ *
+ *
+ * Copyright (c) 2006-2010, PostgreSQL Global Development Group
+ *
+ * src/include/catalog/pg_range.h
+ *
+ * NOTES
+ *	  the genbki.pl script reads this file and generates .bki
+ *	  information from the DATA() statements.
+ *
+ *	  XXX do NOT break up DATA() statements into multiple lines!
+ *		  the scripts are not as smart as you might think...
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_RANGE_H
+#define PG_RANGE_H
+
+#include "catalog/genbki.h"
+
+/* ----------------
+ *		pg_range definition.  cpp turns this into
+ *		typedef struct FormData_pg_range
+ * ----------------
+ */
+#define RangeRelationId	3541
+
+CATALOG(pg_range,3541) BKI_WITHOUT_OIDS
+{
+	Oid			rngtypid;		/* OID of owning range type */
+	Oid			rngsubtype;		/* OID of range's subtype */
+	Oid			rngcollation;	/* collation for this range type, or 0 */
+	Oid			rngsubopc;		/* subtype's btree opclass */
+	regproc		rngcanonical;	/* canonicalize range, or 0 */
+	regproc		rngsubdiff;		/* subtype difference as a float8 (for GiST) */
+} FormData_pg_range;
+
+/* ----------------
+ *		Form_pg_range corresponds to a pointer to a tuple with
+ *		the format of pg_range relation.
+ * ----------------
+ */
+typedef FormData_pg_range *Form_pg_range;
+
+/* ----------------
+ *		compiler constants for pg_range
+ * ----------------
+ */
+#define Natts_pg_range					6
+#define Anum_pg_range_rngtypid			1
+#define Anum_pg_range_rngsubtype		2
+#define Anum_pg_range_rngcollation		3
+#define Anum_pg_range_rngsubopc			4
+#define Anum_pg_range_rngcanonical		5
+#define Anum_pg_range_rngsubdiff		6
+
+#define RANGE_DEFAULT_FLAGS		"[)"
+
+/*
+ * prototypes for functions in pg_range.c
+ */
+
+extern void RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
+			Oid rangeSubOpclass, RegProcedure rangeCanonical,
+			RegProcedure rangeSubDiff);
+extern void RangeDelete(Oid rangeTypeOid);
+
+/* ----------------
+ *		initial contents of pg_range
+ * ----------------
+ */
+DATA(insert ( 3904 23 0 1978 int4range_canonical int4range_subdiff));
+DATA(insert ( 3906 1700 0 10037 - numrange_subdiff));
+DATA(insert ( 3908 1114 0 10054 - tsrange_subdiff));
+DATA(insert ( 3910 1184 0 10047 - tstzrange_subdiff));
+DATA(insert ( 3912 1082 0 10019 daterange_canonical daterange_subdiff));
+DATA(insert ( 3926 20 0 10029 int8range_canonical int8range_subdiff));
+
+#endif   /* PG_RANGE_H */
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index d72ca2342fabb33239ceed03cb85ecaeacd0fe7c..b24fbc97f4e71182a6c9d59bc41244d2cf0b310c 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -591,6 +591,28 @@ DATA(insert OID = 2970 ( txid_snapshot	PGNSP PGUID -1 f b U f t \054 0 0 2949 tx
 DESCR("txid snapshot");
 DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 0 _null_ _null_ ));
 
+/* range types */
+
+DATA(insert OID = 3904 ( int4range		PGNSP PGUID  -1 f r R f t \054 0 0 3905 range_in range_out range_recv range_send - - - i x f 0 -1 0 0 _null_ _null_ ));
+DESCR("range of int4s");
+#define INT4RANGEOID		3904
+DATA(insert OID = 3905 ( _int4range		PGNSP PGUID  -1 f b A f t \054 0 3904 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ ));
+DATA(insert OID = 3906 ( numrange		PGNSP PGUID  -1 f r R f t \054 0 0 3907 range_in range_out range_recv range_send - - - i x f 0 -1 0 0 _null_ _null_ ));
+DESCR("range of numerics");
+DATA(insert OID = 3907 ( _numrange		PGNSP PGUID  -1 f b A f t \054 0 3906 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ ));
+DATA(insert OID = 3908 ( tsrange		PGNSP PGUID  -1 f r R f t \054 0 0 3909 range_in range_out range_recv range_send - - - i x f 0 -1 0 0 _null_ _null_ ));
+DESCR("range of timestamps");
+DATA(insert OID = 3909 ( _tsrange		PGNSP PGUID  -1 f b A f t \054 0 3908 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ ));
+DATA(insert OID = 3910 ( tstzrange		PGNSP PGUID  -1 f r R f t \054 0 0 3911 range_in range_out range_recv range_send - - - i x f 0 -1 0 0 _null_ _null_ ));
+DESCR("range of timestamps with time zone");
+DATA(insert OID = 3911 ( _tstzrange		PGNSP PGUID  -1 f b A f t \054 0 3910 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ ));
+DATA(insert OID = 3912 ( daterange		PGNSP PGUID  -1 f r R f t \054 0 0 3913 range_in range_out range_recv range_send - - - i x f 0 -1 0 0 _null_ _null_ ));
+DESCR("range of dates");
+DATA(insert OID = 3913 ( _daterange		PGNSP PGUID  -1 f b A f t \054 0 3912 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ ));
+DATA(insert OID = 3926 ( int8range		PGNSP PGUID  -1 f r R f t \054 0 0 3927 range_in range_out range_recv range_send - - - i x f 0 -1 0 0 _null_ _null_ ));
+DESCR("range of int8s");
+DATA(insert OID = 3927 ( _int8range		PGNSP PGUID  -1 f b A f t \054 0 3926 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ ));
+
 /*
  * pseudo-types
  *
@@ -632,6 +654,8 @@ DATA(insert OID = 3500 ( anyenum		PGNSP PGUID  4 t p P f t \054 0 0 0 anyenum_in
 #define ANYENUMOID		3500
 DATA(insert OID = 3115 ( fdw_handler	PGNSP PGUID  4 t p P f t \054 0 0 0 fdw_handler_in fdw_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ ));
 #define FDW_HANDLEROID		3115
+DATA(insert OID = 3831 ( anyrange		PGNSP PGUID  4 t p P f t \054 0 0 0 anyrange_in anyrange_out - - - - - i p f 0 -1 0 0 _null_ _null_ ));
+#define ANYRANGEOID		3831
 
 
 /*
@@ -642,6 +666,7 @@ DATA(insert OID = 3115 ( fdw_handler	PGNSP PGUID  4 t p P f t \054 0 0 0 fdw_han
 #define  TYPTYPE_DOMAIN		'd' /* domain over another type */
 #define  TYPTYPE_ENUM		'e' /* enumerated type */
 #define  TYPTYPE_PSEUDO		'p' /* pseudo-type */
+#define  TYPTYPE_RANGE		'r' /* range type */
 
 #define  TYPCATEGORY_INVALID	'\0'	/* not an allowed category */
 #define  TYPCATEGORY_ARRAY		'A'
@@ -653,6 +678,7 @@ DATA(insert OID = 3115 ( fdw_handler	PGNSP PGUID  4 t p P f t \054 0 0 0 fdw_han
 #define  TYPCATEGORY_NETWORK	'I'		/* think INET */
 #define  TYPCATEGORY_NUMERIC	'N'
 #define  TYPCATEGORY_PSEUDOTYPE 'P'
+#define  TYPCATEGORY_RANGE		'R'
 #define  TYPCATEGORY_STRING		'S'
 #define  TYPCATEGORY_TIMESPAN	'T'
 #define  TYPCATEGORY_USER		'U'
@@ -664,6 +690,7 @@ DATA(insert OID = 3115 ( fdw_handler	PGNSP PGUID  4 t p P f t \054 0 0 0 fdw_han
 	((typid) == ANYELEMENTOID || \
 	 (typid) == ANYARRAYOID || \
 	 (typid) == ANYNONARRAYOID || \
-	 (typid) == ANYENUMOID)
+	 (typid) == ANYENUMOID || \
+	 (typid) == ANYRANGEOID)
 
 #endif   /* PG_TYPE_H */
diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h
index 429a964f913fa2dc145bd384a036d10419d49cca..0c328958635ed250a44c610b802e05fd4c12ef63 100644
--- a/src/include/commands/typecmds.h
+++ b/src/include/commands/typecmds.h
@@ -23,6 +23,7 @@ extern void DefineType(List *names, List *parameters);
 extern void RemoveTypeById(Oid typeOid);
 extern void DefineDomain(CreateDomainStmt *stmt);
 extern void DefineEnum(CreateEnumStmt *stmt);
+extern void DefineRange(CreateRangeStmt *stmt);
 extern void AlterEnum(AlterEnumStmt *stmt);
 extern Oid	DefineCompositeType(const RangeVar *typevar, List *coldeflist);
 extern Oid	AssignTypeArrayOid(void);
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 7aa299485fcaff78c64afd626c2e917ccb6a4773..824d8b5dc9a24146fe520806a37b20edff02d262 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -346,6 +346,7 @@ typedef enum NodeTag
 	T_ReassignOwnedStmt,
 	T_CompositeTypeStmt,
 	T_CreateEnumStmt,
+	T_CreateRangeStmt,
 	T_AlterEnumStmt,
 	T_AlterTSDictionaryStmt,
 	T_AlterTSConfigurationStmt,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 9998e2f24d6793ca17b6fd0ec93f48d4643898aa..af6565e7e4a805857ff18d6da2e46e210aad4c09 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2334,6 +2334,17 @@ typedef struct AlterEnumStmt
 	bool		newValIsAfter;	/* place new enum value after neighbor? */
 } AlterEnumStmt;
 
+/* ----------------------
+ *		Create Type Statement, range types
+ * ----------------------
+ */
+typedef struct CreateRangeStmt
+{
+	NodeTag		type;
+	List	   *typeName;		/* qualified name (list of Value strings) */
+	List	   *params;			/* range parameters (list of DefElem) */
+} CreateRangeStmt;
+
 /* ----------------------
  *		Create View Statement
  * ----------------------
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index 215951589af3d19c40262bf2892ed1cfeef12c23..d3ad4f14282641fc7ba35f3188d76a8b097dd22e 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -119,6 +119,7 @@ extern Node *get_typdefault(Oid typid);
 extern char get_typtype(Oid typid);
 extern bool type_is_rowtype(Oid typid);
 extern bool type_is_enum(Oid typid);
+extern bool type_is_range(Oid typid);
 extern void get_type_category_preferred(Oid typid,
 							char *typcategory,
 							bool *typispreferred);
@@ -147,6 +148,7 @@ extern void free_attstatsslot(Oid atttype,
 				  Datum *values, int nvalues,
 				  float4 *numbers, int nnumbers);
 extern char *get_namespace_name(Oid nspid);
+extern Oid get_range_subtype(Oid rangeOid);
 
 #define type_is_array(typid)  (get_element_type(typid) != InvalidOid)
 /* type_is_array_domain accepts both plain arrays and domains over arrays */
diff --git a/src/include/utils/rangetypes.h b/src/include/utils/rangetypes.h
new file mode 100644
index 0000000000000000000000000000000000000000..a7595b15f6019f9efd84a11a85ac6308af6ef7fb
--- /dev/null
+++ b/src/include/utils/rangetypes.h
@@ -0,0 +1,159 @@
+/*-------------------------------------------------------------------------
+ *
+ * rangetypes.h
+ *	  Declarations for Postgres range types.
+ *
+ */
+
+#ifndef RANGETYPES_H
+#define RANGETYPES_H
+
+#include "fmgr.h"
+
+typedef struct varlena RangeType;
+
+typedef struct
+{
+	Datum		val;
+	Oid			rngtypid;
+	bool		infinite;
+	bool		lower;
+	bool		inclusive;
+} RangeBound;
+
+typedef struct
+{
+	FmgrInfo	canonicalFn;
+	FmgrInfo	cmpFn;
+	FmgrInfo	subdiffFn;
+	Oid			rngtypid;
+	Oid			subtype;
+	Oid			collation;
+	int16		subtyplen;
+	char		subtypalign;
+	char		subtypstorage;
+	bool		subtypbyval;
+} RangeTypeInfo;
+
+/*
+ * fmgr macros for range type objects
+ */
+#define DatumGetRangeType(X)			((RangeType *) PG_DETOAST_DATUM(X))
+#define DatumGetRangeTypeCopy(X)		((RangeType *) PG_DETOAST_DATUM_COPY(X))
+#define RangeTypeGetDatum(X)			PointerGetDatum(X)
+#define PG_GETARG_RANGE(n)				DatumGetRangeType(PG_GETARG_DATUM(n))
+#define PG_GETARG_RANGE_COPY(n)			DatumGetRangeTypeCopy(PG_GETARG_DATUM(n))
+#define PG_RETURN_RANGE(x)				return RangeTypeGetDatum(x)
+
+/*
+ * prototypes for functions defined in rangetypes.c
+ */
+
+/* IO */
+extern Datum anyrange_in(PG_FUNCTION_ARGS);
+extern Datum anyrange_out(PG_FUNCTION_ARGS);
+extern Datum range_in(PG_FUNCTION_ARGS);
+extern Datum range_out(PG_FUNCTION_ARGS);
+extern Datum range_recv(PG_FUNCTION_ARGS);
+extern Datum range_send(PG_FUNCTION_ARGS);
+
+/* constructors */
+extern Datum range_constructor0(PG_FUNCTION_ARGS);
+extern Datum range_constructor1(PG_FUNCTION_ARGS);
+extern Datum range_constructor2(PG_FUNCTION_ARGS);
+extern Datum range_constructor3(PG_FUNCTION_ARGS);
+extern Datum range_make1(PG_FUNCTION_ARGS);
+extern Datum range_linf_(PG_FUNCTION_ARGS);
+extern Datum range_uinf_(PG_FUNCTION_ARGS);
+extern Datum range_linfi(PG_FUNCTION_ARGS);
+extern Datum range_uinfi(PG_FUNCTION_ARGS);
+extern Datum range(PG_FUNCTION_ARGS);
+extern Datum range__(PG_FUNCTION_ARGS);
+extern Datum range_i(PG_FUNCTION_ARGS);
+extern Datum rangei_(PG_FUNCTION_ARGS);
+extern Datum rangeii(PG_FUNCTION_ARGS);
+
+/* range -> subtype */
+extern Datum range_lower(PG_FUNCTION_ARGS);
+extern Datum range_upper(PG_FUNCTION_ARGS);
+
+/* range -> bool */
+extern Datum range_empty(PG_FUNCTION_ARGS);
+extern Datum range_lower_inc(PG_FUNCTION_ARGS);
+extern Datum range_upper_inc(PG_FUNCTION_ARGS);
+extern Datum range_lower_inf(PG_FUNCTION_ARGS);
+extern Datum range_upper_inf(PG_FUNCTION_ARGS);
+
+/* range, point -> bool */
+extern Datum range_contains_elem(PG_FUNCTION_ARGS);
+extern Datum elem_contained_by_range(PG_FUNCTION_ARGS);
+
+/* range, range -> bool */
+extern Datum range_eq(PG_FUNCTION_ARGS);
+extern Datum range_ne(PG_FUNCTION_ARGS);
+extern Datum range_contains(PG_FUNCTION_ARGS);
+extern Datum range_contained_by(PG_FUNCTION_ARGS);
+extern Datum range_before(PG_FUNCTION_ARGS);
+extern Datum range_after(PG_FUNCTION_ARGS);
+extern Datum range_adjacent(PG_FUNCTION_ARGS);
+extern Datum range_overlaps(PG_FUNCTION_ARGS);
+extern Datum range_overleft(PG_FUNCTION_ARGS);
+extern Datum range_overright(PG_FUNCTION_ARGS);
+
+/* range, range -> range */
+extern Datum range_minus(PG_FUNCTION_ARGS);
+extern Datum range_union(PG_FUNCTION_ARGS);
+extern Datum range_intersect(PG_FUNCTION_ARGS);
+
+/* BTree support */
+extern Datum range_cmp(PG_FUNCTION_ARGS);
+extern Datum range_lt(PG_FUNCTION_ARGS);
+extern Datum range_le(PG_FUNCTION_ARGS);
+extern Datum range_ge(PG_FUNCTION_ARGS);
+extern Datum range_gt(PG_FUNCTION_ARGS);
+
+/* Hash support */
+extern Datum hash_range(PG_FUNCTION_ARGS);
+
+/* GiST support (rangetypes_gist.c) */
+extern Datum range_gist_consistent(PG_FUNCTION_ARGS);
+extern Datum range_gist_compress(PG_FUNCTION_ARGS);
+extern Datum range_gist_decompress(PG_FUNCTION_ARGS);
+extern Datum range_gist_union(PG_FUNCTION_ARGS);
+extern Datum range_gist_penalty(PG_FUNCTION_ARGS);
+extern Datum range_gist_picksplit(PG_FUNCTION_ARGS);
+extern Datum range_gist_same(PG_FUNCTION_ARGS);
+
+/* Canonical functions */
+Datum int4range_canonical(PG_FUNCTION_ARGS);
+Datum int8range_canonical(PG_FUNCTION_ARGS);
+Datum daterange_canonical(PG_FUNCTION_ARGS);
+
+/* Subtype Difference functions */
+Datum int4range_subdiff(PG_FUNCTION_ARGS);
+Datum int8range_subdiff(PG_FUNCTION_ARGS);
+Datum numrange_subdiff(PG_FUNCTION_ARGS);
+Datum daterange_subdiff(PG_FUNCTION_ARGS);
+Datum tsrange_subdiff(PG_FUNCTION_ARGS);
+Datum tstzrange_subdiff(PG_FUNCTION_ARGS);
+
+/* for defining more generic functions */
+extern Datum make_range(FunctionCallInfo fcinfo, RangeBound *lower,
+						RangeBound *upper, bool empty);
+extern void range_deserialize(FunctionCallInfo fcinfo, RangeType *range,
+							  RangeBound *lower, RangeBound *upper,
+							  bool *empty);
+extern int range_cmp_bounds(FunctionCallInfo fcinfo, RangeBound *b1,
+							RangeBound *b2);
+extern RangeType *make_empty_range(FunctionCallInfo fcinfo, Oid rngtypid);
+extern void range_gettypinfo(FunctionCallInfo fcinfo, Oid rngtypid,
+							 RangeTypeInfo *rngtypinfo);
+
+/* for defining a range "canonicalize" function */
+extern Datum range_serialize(FunctionCallInfo fcinfo, RangeBound *lower,
+							 RangeBound *upper, bool empty);
+
+/* for use in DefineRange */
+extern char range_parse_flags(char *flags_str);
+
+#endif   /* RANGETYPES_H */
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index 55d22303a7ac1c166a15dfea8d4554554b5d8cb8..35782fc3e7b28c31e3702ca4a43e5271a139e091 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -70,6 +70,7 @@ enum SysCacheIdentifier
 	OPFAMILYOID,
 	PROCNAMEARGSNSP,
 	PROCOID,
+	RANGETYPE,
 	RELNAMENSP,
 	RELOID,
 	RULERELNAME,
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index 578cae57346f46cf11404499fd89630db239e1fe..48399d3929ceb1b36b6ebb693be16695814bd2d0 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -490,6 +490,8 @@ do_compile(FunctionCallInfo fcinfo,
 				{
 					if (rettypeid == ANYARRAYOID)
 						rettypeid = INT4ARRAYOID;
+					else if (rettypeid == ANYRANGEOID)
+						rettypeid = INT4RANGEOID;
 					else	/* ANYELEMENT or ANYNONARRAY */
 						rettypeid = INT4OID;
 					/* XXX what could we use for ANYENUM? */
@@ -2119,6 +2121,7 @@ build_datatype(HeapTuple typeTup, int32 typmod, Oid collation)
 		case TYPTYPE_BASE:
 		case TYPTYPE_DOMAIN:
 		case TYPTYPE_ENUM:
+		case TYPTYPE_RANGE:
 			typ->ttype = PLPGSQL_TTYPE_SCALAR;
 			break;
 		case TYPTYPE_COMPOSITE:
@@ -2373,8 +2376,8 @@ compute_function_hashkey(FunctionCallInfo fcinfo,
 /*
  * This is the same as the standard resolve_polymorphic_argtypes() function,
  * but with a special case for validation: assume that polymorphic arguments
- * are integer or integer-array.  Also, we go ahead and report the error
- * if we can't resolve the types.
+ * are integer, integer-range or integer-array.  Also, we go ahead and report
+ * the error if we can't resolve the types.
  */
 static void
 plpgsql_resolve_polymorphic_argtypes(int numargs,
@@ -2407,6 +2410,9 @@ plpgsql_resolve_polymorphic_argtypes(int numargs,
 				case ANYENUMOID:		/* XXX dubious */
 					argtypes[i] = INT4OID;
 					break;
+				case ANYRANGEOID:
+					argtypes[i] = INT4RANGEOID;
+					break;
 				case ANYARRAYOID:
 					argtypes[i] = INT4ARRAYOID;
 					break;
diff --git a/src/test/regress/expected/collate.linux.utf8.out b/src/test/regress/expected/collate.linux.utf8.out
index 8cee6ed81275736c0f7e9e5313596b8d3aa60277..f9659f7739e256fdaf1288394012984878791440 100644
--- a/src/test/regress/expected/collate.linux.utf8.out
+++ b/src/test/regress/expected/collate.linux.utf8.out
@@ -1049,3 +1049,20 @@ Composite type "public.collate_dep_test2"
 
 DROP TABLE collate_dep_test1, collate_dep_test4t;
 DROP TYPE collate_dep_test2;
+-- test range types and collations
+create type textrange_c as range(subtype=text, collation="C");
+create type textrange_en_us as range(subtype=text, collation="en_US");
+select textrange_c('A','Z') @> 'b'::text;
+ ?column? 
+----------
+ f
+(1 row)
+
+select textrange_en_us('A','Z') @> 'b'::text;
+ ?column? 
+----------
+ t
+(1 row)
+
+drop type textrange_c;
+drop type textrange_en_us;
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index a25f90cbfd28ff82c446fc326a8da79a66cd00a5..19b559ffa17d1bc52230bc277019b4e2260342ac 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -147,7 +147,9 @@ WHERE p1.oid != p2.oid AND
     p1.prosrc = p2.prosrc AND
     p1.prolang = 12 AND p2.prolang = 12 AND
     NOT p1.proisagg AND NOT p2.proisagg AND
-    (p1.prorettype < p2.prorettype)
+    (p1.prorettype < p2.prorettype) AND
+    -- range constructor functions are shared by all range types.
+    NOT p1.prosrc LIKE 'range_constructor%'
 ORDER BY 1, 2;
  prorettype | prorettype 
 ------------+------------
@@ -161,7 +163,9 @@ WHERE p1.oid != p2.oid AND
     p1.prosrc = p2.prosrc AND
     p1.prolang = 12 AND p2.prolang = 12 AND
     NOT p1.proisagg AND NOT p2.proisagg AND
-    (p1.proargtypes[0] < p2.proargtypes[0])
+    (p1.proargtypes[0] < p2.proargtypes[0]) AND
+    -- range constructor functions are shared by all range types.
+    NOT p1.prosrc LIKE 'range_constructor%'
 ORDER BY 1, 2;
  proargtypes | proargtypes 
 -------------+-------------
@@ -178,7 +182,9 @@ WHERE p1.oid != p2.oid AND
     p1.prosrc = p2.prosrc AND
     p1.prolang = 12 AND p2.prolang = 12 AND
     NOT p1.proisagg AND NOT p2.proisagg AND
-    (p1.proargtypes[1] < p2.proargtypes[1])
+    (p1.proargtypes[1] < p2.proargtypes[1]) AND
+    -- range constructor functions are shared by all range types.
+    NOT p1.prosrc LIKE 'range_constructor%'
 ORDER BY 1, 2;
  proargtypes | proargtypes 
 -------------+-------------
@@ -1015,19 +1021,30 @@ ORDER BY 1, 2, 3;
         403 |            5 | ~>~
         405 |            1 | =
         783 |            1 | <<
+        783 |            1 | =
         783 |            1 | @@
         783 |            2 | &<
+        783 |            2 | <>
         783 |            3 | &&
         783 |            4 | &>
+        783 |            4 | @>
+        783 |            5 | <@
         783 |            5 | >>
+        783 |            6 | @>
         783 |            6 | ~=
+        783 |            7 | <@
         783 |            7 | @>
+        783 |            8 | <<
         783 |            8 | <@
         783 |            9 | &<|
+        783 |            9 | >>
+        783 |           10 | &<
         783 |           10 | <<|
         783 |           10 | <^
+        783 |           11 | &>
         783 |           11 | >^
         783 |           11 | |>>
+        783 |           12 | -|-
         783 |           12 | |&>
         783 |           13 | ~
         783 |           14 | @
@@ -1044,7 +1061,7 @@ ORDER BY 1, 2, 3;
        2742 |            2 | @@@
        2742 |            3 | <@
        2742 |            4 | =
-(40 rows)
+(51 rows)
 
 -- Check that all opclass search operators have selectivity estimators.
 -- This is not absolutely required, but it seems a reasonable thing
@@ -1053,9 +1070,15 @@ SELECT p1.amopfamily, p1.amopopr, p2.oid, p2.oprname
 FROM pg_amop AS p1, pg_operator AS p2
 WHERE p1.amopopr = p2.oid AND p1.amoppurpose = 's' AND
     (p2.oprrest = 0 OR p2.oprjoin = 0);
- amopfamily | amopopr | oid | oprname 
-------------+---------+-----+---------
-(0 rows)
+ amopfamily | amopopr | oid  | oprname 
+------------+---------+------+---------
+       3919 |    3888 | 3888 | &&
+       3919 |    3889 | 3889 | @>
+       3919 |    3891 | 3891 | <@
+       3919 |    3890 | 3890 | @>
+       3919 |    3892 | 3892 | <@
+       3919 |    3897 | 3897 | -|-
+(6 rows)
 
 -- Check that each opclass in an opfamily has associated operators, that is
 -- ones whose oprleft matches opcintype (possibly by coercion).
diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out
index 238bf5f0aec5e85e6af8598e4174797116644e25..fc9d4019444a733d768827afa9c699c404636c57 100644
--- a/src/test/regress/expected/plpgsql.out
+++ b/src/test/regress/expected/plpgsql.out
@@ -4571,3 +4571,17 @@ ERROR:  value for domain orderedarray violates check constraint "sorted"
 CONTEXT:  PL/pgSQL function "testoa" line 5 at assignment
 drop function arrayassign1();
 drop function testoa(x1 int, x2 int, x3 int);
+-- Test resolve_polymorphic_argtypes() codepath. It is only taken when
+-- a function is invoked from a different backend from where it's defined,
+-- so we create the a function with polymorphic argument, reconnect, and
+-- and then call it.
+create function rangetypes_plpgsql(out a anyelement, b anyrange, c anyarray)
+  language plpgsql as
+  $$ begin a := upper(b) + c[1]; return; end; $$;
+\c -
+select rangetypes_plpgsql(int4range(1,10),ARRAY[2,20]);
+ rangetypes_plpgsql 
+--------------------
+                 12
+(1 row)
+
diff --git a/src/test/regress/expected/rangetypes.out b/src/test/regress/expected/rangetypes.out
new file mode 100644
index 0000000000000000000000000000000000000000..495508c42ec74bdeb66092c04d6c6ceeb1e03c01
--- /dev/null
+++ b/src/test/regress/expected/rangetypes.out
@@ -0,0 +1,951 @@
+--
+-- test parser
+--
+create type textrange as range (subtype=text, collation="C");
+-- negative tests; should fail
+select ''::textrange;
+ERROR:  malformed range literal: ""
+LINE 1: select ''::textrange;
+               ^
+DETAIL:  Missing left parenthesis or bracket.
+select '-[a,z)'::textrange;
+ERROR:  malformed range literal: "-[a,z)"
+LINE 1: select '-[a,z)'::textrange;
+               ^
+DETAIL:  Missing left parenthesis or bracket.
+select '[a,z) - '::textrange;
+ERROR:  malformed range literal: "[a,z) - "
+LINE 1: select '[a,z) - '::textrange;
+               ^
+DETAIL:  Junk after right parenthesis or bracket.
+select '(",a)'::textrange;
+ERROR:  malformed range literal: "(",a)"
+LINE 1: select '(",a)'::textrange;
+               ^
+DETAIL:  Unexpected end of input.
+select '(,,a)'::textrange;
+ERROR:  malformed range literal: "(,,a)"
+LINE 1: select '(,,a)'::textrange;
+               ^
+DETAIL:  Too many boundaries.
+select '(),a)'::textrange;
+ERROR:  malformed range literal: "(),a)"
+LINE 1: select '(),a)'::textrange;
+               ^
+DETAIL:  Missing upper bound.
+select '(a,))'::textrange;
+ERROR:  malformed range literal: "(a,))"
+LINE 1: select '(a,))'::textrange;
+               ^
+DETAIL:  Junk after right parenthesis or bracket.
+select '(],a)'::textrange;
+ERROR:  malformed range literal: "(],a)"
+LINE 1: select '(],a)'::textrange;
+               ^
+DETAIL:  Missing upper bound.
+select '(a,])'::textrange;
+ERROR:  malformed range literal: "(a,])"
+LINE 1: select '(a,])'::textrange;
+               ^
+DETAIL:  Junk after right parenthesis or bracket.
+-- should succeed
+select '  empty  '::textrange;
+ textrange 
+-----------
+ empty
+(1 row)
+
+select ' ( empty, empty )  '::textrange;
+      textrange       
+----------------------
+ (" empty"," empty ")
+(1 row)
+
+select ' ( " a " " a ", " z " " z " )  '::textrange;
+        textrange         
+--------------------------
+ ("  a   a ","  z   z  ")
+(1 row)
+
+select '(,z)'::textrange;
+ textrange 
+-----------
+ (,z)
+(1 row)
+
+select '(a,)'::textrange;
+ textrange 
+-----------
+ (a,)
+(1 row)
+
+select '[,z]'::textrange;
+ textrange 
+-----------
+ (,z]
+(1 row)
+
+select '[a,]'::textrange;
+ textrange 
+-----------
+ [a,)
+(1 row)
+
+select '( , )'::textrange;
+ textrange 
+-----------
+ (" "," ")
+(1 row)
+
+select '("","")'::textrange;
+ textrange 
+-----------
+ ("","")
+(1 row)
+
+select '["",""]'::textrange;
+ textrange 
+-----------
+ ["",""]
+(1 row)
+
+select '(",",",")'::textrange;
+ textrange 
+-----------
+ (",",",")
+(1 row)
+
+select '("\\","\\")'::textrange
+select '(\\,a)'::textrange;
+ERROR:  syntax error at or near "select"
+LINE 2: select '(\\,a)'::textrange;
+        ^
+select '((,z)'::textrange;
+ textrange 
+-----------
+ ("(",z)
+(1 row)
+
+select '([,z)'::textrange;
+ textrange 
+-----------
+ ("[",z)
+(1 row)
+
+select '(!,()'::textrange;
+ textrange 
+-----------
+ (!,"(")
+(1 row)
+
+select '(!,[)'::textrange;
+ textrange 
+-----------
+ (!,"[")
+(1 row)
+
+drop type textrange;
+--
+-- create some test data and test the operators
+--
+CREATE TABLE numrange_test (nr NUMRANGE);
+create index numrange_test_btree on numrange_test(nr);
+SET enable_seqscan = f;
+INSERT INTO numrange_test VALUES('[,)');
+INSERT INTO numrange_test VALUES('[3,]');
+INSERT INTO numrange_test VALUES('[, 5)');
+INSERT INTO numrange_test VALUES(numrange(1.1, 2.2));
+INSERT INTO numrange_test VALUES('empty');
+INSERT INTO numrange_test VALUES(numrange(1.7));
+SELECT isempty(nr) FROM numrange_test;
+ isempty 
+---------
+ f
+ f
+ f
+ f
+ t
+ f
+(6 rows)
+
+SELECT lower_inc(nr), lower(nr), upper(nr), upper_inc(nr) FROM numrange_test
+  WHERE NOT isempty(nr) AND NOT lower_inf(nr) AND NOT upper_inf(nr);
+ lower_inc | lower | upper | upper_inc 
+-----------+-------+-------+-----------
+ t         |   1.1 |   2.2 | f
+ t         |   1.7 |   1.7 | t
+(2 rows)
+
+SELECT * FROM numrange_test WHERE contains(nr, numrange(1.9,1.91));
+    nr     
+-----------
+ (,)
+ (,5)
+ [1.1,2.2)
+(3 rows)
+
+SELECT * FROM numrange_test WHERE nr @> numrange(1.0,10000.1);
+ nr  
+-----
+ (,)
+(1 row)
+
+SELECT * FROM numrange_test WHERE contained_by(numrange(-1e7,-10000.1), nr);
+  nr  
+------
+ (,)
+ (,5)
+(2 rows)
+
+SELECT * FROM numrange_test WHERE 1.9 <@ nr;
+    nr     
+-----------
+ (,)
+ (,5)
+ [1.1,2.2)
+(3 rows)
+
+SELECT * FROM numrange_test WHERE nr = 'empty';
+  nr   
+-------
+ empty
+(1 row)
+
+SELECT * FROM numrange_test WHERE range_eq(nr, '(1.1, 2.2)');
+ nr 
+----
+(0 rows)
+
+SELECT * FROM numrange_test WHERE nr = '[1.1, 2.2)';
+    nr     
+-----------
+ [1.1,2.2)
+(1 row)
+
+select numrange(2.0, 1.0);
+ERROR:  range lower bound must be less than or equal to range upper bound
+select numrange(2.0, 3.0) -|- numrange(3.0, 4.0);
+ ?column? 
+----------
+ t
+(1 row)
+
+select adjacent(numrange(2.0, 3.0), numrange(3.1, 4.0));
+ adjacent 
+----------
+ f
+(1 row)
+
+select numrange(2.0, 3.0, '[]') -|- numrange(3.0, 4.0, '()');
+ ?column? 
+----------
+ t
+(1 row)
+
+select numrange(1.0, 2.0) -|- numrange(2.0, 3.0,'[]');
+ ?column? 
+----------
+ t
+(1 row)
+
+select adjacent(numrange(2.0, 3.0, '(]'), numrange(1.0, 2.0, '(]'));
+ adjacent 
+----------
+ t
+(1 row)
+
+select numrange(1.1, 3.3) <@ numrange(0.1,10.1);
+ ?column? 
+----------
+ t
+(1 row)
+
+select numrange(0.1, 10.1) <@ numrange(1.1,3.3);
+ ?column? 
+----------
+ f
+(1 row)
+
+select numrange(1.1, 2.2) - numrange(2.0, 3.0);
+ ?column?  
+-----------
+ [1.1,2.0)
+(1 row)
+
+select numrange(1.1, 2.2) - numrange(2.2, 3.0);
+ ?column?  
+-----------
+ [1.1,2.2)
+(1 row)
+
+select numrange(1.1, 2.2,'[]') - numrange(2.0, 3.0);
+ ?column?  
+-----------
+ [1.1,2.0)
+(1 row)
+
+select minus(numrange(10.1,12.2,'[]'), numrange(110.0,120.2,'(]'));
+    minus    
+-------------
+ [10.1,12.2]
+(1 row)
+
+select minus(numrange(10.1,12.2,'[]'), numrange(0.0,120.2,'(]'));
+ minus 
+-------
+ empty
+(1 row)
+
+select numrange(4.5, 5.5, '[]') && numrange(5.5, 6.5);
+ ?column? 
+----------
+ t
+(1 row)
+
+select numrange(1.0, 2.0) << numrange(3.0, 4.0);
+ ?column? 
+----------
+ t
+(1 row)
+
+select numrange(1.0, 2.0) >> numrange(3.0, 4.0);
+ ?column? 
+----------
+ f
+(1 row)
+
+select numrange(3.0, 70.0) &< numrange(6.6, 100.0);
+ ?column? 
+----------
+ t
+(1 row)
+
+select numrange(1.1, 2.2) < numrange(1.0, 200.2);
+ ?column? 
+----------
+ f
+(1 row)
+
+select numrange(1.1, 2.2) < numrange(1.1, 1.2);
+ ?column? 
+----------
+ f
+(1 row)
+
+select numrange(1.0, 2.0) + numrange(2.0, 3.0);
+ ?column?  
+-----------
+ [1.0,3.0)
+(1 row)
+
+select numrange(1.0, 2.0) + numrange(1.5, 3.0);
+ ?column?  
+-----------
+ [1.0,3.0)
+(1 row)
+
+select numrange(1.0, 2.0) + numrange(2.5, 3.0);
+ERROR:  result range is not contiguous
+select numrange(1.0, 2.0) * numrange(2.0, 3.0);
+ ?column? 
+----------
+ empty
+(1 row)
+
+select numrange(1.0, 2.0) * numrange(1.5, 3.0);
+ ?column?  
+-----------
+ [1.5,2.0)
+(1 row)
+
+select numrange(1.0, 2.0) * numrange(2.5, 3.0);
+ ?column? 
+----------
+ empty
+(1 row)
+
+select * from numrange_test where nr < numrange(-1000.0, -1000.0,'[]');
+  nr   
+-------
+ (,)
+ (,5)
+ empty
+(3 rows)
+
+select * from numrange_test where nr < numrange(0.0, 1.0,'[]');
+  nr   
+-------
+ (,)
+ (,5)
+ empty
+(3 rows)
+
+select * from numrange_test where nr < numrange(1000.0, 1001.0,'[]');
+    nr     
+-----------
+ (,)
+ [3,)
+ (,5)
+ [1.1,2.2)
+ empty
+ [1.7,1.7]
+(6 rows)
+
+select * from numrange_test where nr > numrange(-1001.0, -1000.0,'[]');
+    nr     
+-----------
+ [3,)
+ [1.1,2.2)
+ [1.7,1.7]
+(3 rows)
+
+select * from numrange_test where nr > numrange(0.0, 1.0,'[]');
+    nr     
+-----------
+ [3,)
+ [1.1,2.2)
+ [1.7,1.7]
+(3 rows)
+
+select * from numrange_test where nr > numrange(1000.0, 1000.0,'[]');
+ nr 
+----
+(0 rows)
+
+create table numrange_test2(nr numrange);
+create index numrange_test2_hash_idx on numrange_test2 (nr);
+INSERT INTO numrange_test2 VALUES('[, 5)');
+INSERT INTO numrange_test2 VALUES(numrange(1.1, 2.2));
+INSERT INTO numrange_test2 VALUES(numrange(1.1, 2.2));
+INSERT INTO numrange_test2 VALUES(numrange(1.1, 2.2,'()'));
+INSERT INTO numrange_test2 VALUES('empty');
+select * from numrange_test2 where nr = 'empty'::numrange;
+  nr   
+-------
+ empty
+(1 row)
+
+select * from numrange_test2 where nr = numrange(1.1, 2.2);
+    nr     
+-----------
+ [1.1,2.2)
+ [1.1,2.2)
+(2 rows)
+
+select * from numrange_test2 where nr = numrange(1.1, 2.3);
+ nr 
+----
+(0 rows)
+
+set enable_nestloop=t;
+set enable_hashjoin=f;
+set enable_mergejoin=f;
+select * from numrange_test natural join numrange_test2 order by nr;
+    nr     
+-----------
+ empty
+ (,5)
+ [1.1,2.2)
+ [1.1,2.2)
+(4 rows)
+
+set enable_nestloop=f;
+set enable_hashjoin=t;
+set enable_mergejoin=f;
+select * from numrange_test natural join numrange_test2 order by nr;
+    nr     
+-----------
+ empty
+ (,5)
+ [1.1,2.2)
+ [1.1,2.2)
+(4 rows)
+
+set enable_nestloop=f;
+set enable_hashjoin=f;
+set enable_mergejoin=t;
+select * from numrange_test natural join numrange_test2 order by nr;
+    nr     
+-----------
+ empty
+ (,5)
+ [1.1,2.2)
+ [1.1,2.2)
+(4 rows)
+
+set enable_nestloop to default;
+set enable_hashjoin to default;
+set enable_mergejoin to default;
+SET enable_seqscan TO DEFAULT;
+DROP TABLE numrange_test;
+DROP TABLE numrange_test2;
+-- test canonical form for int4range
+select int4range(1,10,'[]');
+ int4range 
+-----------
+ [1,11)
+(1 row)
+
+select int4range(1,10,'[)');
+ int4range 
+-----------
+ [1,10)
+(1 row)
+
+select int4range(1,10,'(]');
+ int4range 
+-----------
+ [2,11)
+(1 row)
+
+select int4range(1,10,'[]');
+ int4range 
+-----------
+ [1,11)
+(1 row)
+
+-- test canonical form for daterange
+select daterange('2000-01-10'::date, '2000-01-20'::date,'[]');
+        daterange        
+-------------------------
+ [01-10-2000,01-21-2000)
+(1 row)
+
+select daterange('2000-01-10'::date, '2000-01-20'::date,'[)');
+        daterange        
+-------------------------
+ [01-10-2000,01-20-2000)
+(1 row)
+
+select daterange('2000-01-10'::date, '2000-01-20'::date,'(]');
+        daterange        
+-------------------------
+ [01-11-2000,01-21-2000)
+(1 row)
+
+select daterange('2000-01-10'::date, '2000-01-20'::date,'[]');
+        daterange        
+-------------------------
+ [01-10-2000,01-21-2000)
+(1 row)
+
+create table test_range_gist(ir int4range);
+create index test_range_gist_idx on test_range_gist using gist (ir);
+insert into test_range_gist select int4range(g, g+10) from generate_series(1,2000) g;
+insert into test_range_gist select 'empty'::int4range from generate_series(1,500) g;
+insert into test_range_gist select int4range(g, g+10000) from generate_series(1,1000) g;
+insert into test_range_gist select 'empty'::int4range from generate_series(1,500) g;
+insert into test_range_gist select int4range(NULL,g*10,'(]') from generate_series(1,100) g;
+insert into test_range_gist select int4range(g*10,NULL,'(]') from generate_series(1,100) g;
+insert into test_range_gist select int4range(g, g+10) from generate_series(1,2000) g;
+BEGIN;
+SET LOCAL enable_seqscan    = t;
+SET LOCAL enable_bitmapscan = f;
+SET LOCAL enable_indexscan  = f;
+select count(*) from test_range_gist where ir @> 'empty'::int4range;
+ count 
+-------
+  6200
+(1 row)
+
+select count(*) from test_range_gist where ir = int4range(10,20);
+ count 
+-------
+     2
+(1 row)
+
+select count(*) from test_range_gist where ir @> 10;
+ count 
+-------
+   130
+(1 row)
+
+select count(*) from test_range_gist where ir @> int4range(10,20);
+ count 
+-------
+   111
+(1 row)
+
+select count(*) from test_range_gist where ir && int4range(10,20);
+ count 
+-------
+   158
+(1 row)
+
+select count(*) from test_range_gist where ir <@ int4range(10,50);
+ count 
+-------
+  1062
+(1 row)
+
+select count(*) from (select * from test_range_gist where not isempty(ir)) s where ir << int4range(100,500);
+ count 
+-------
+   189
+(1 row)
+
+select count(*) from (select * from test_range_gist where not isempty(ir)) s where ir >> int4range(100,500);
+ count 
+-------
+  3554
+(1 row)
+
+select count(*) from (select * from test_range_gist where not isempty(ir)) s where ir &< int4range(100,500);
+ count 
+-------
+  1029
+(1 row)
+
+select count(*) from (select * from test_range_gist where not isempty(ir)) s where ir &> int4range(100,500);
+ count 
+-------
+  4794
+(1 row)
+
+select count(*) from (select * from test_range_gist where not isempty(ir)) s where ir -|- int4range(100,500);
+ count 
+-------
+     5
+(1 row)
+
+COMMIT;
+BEGIN;
+SET LOCAL enable_seqscan    = f;
+SET LOCAL enable_bitmapscan = f;
+SET LOCAL enable_indexscan  = t;
+select count(*) from test_range_gist where ir @> 'empty'::int4range;
+ count 
+-------
+  6200
+(1 row)
+
+select count(*) from test_range_gist where ir = int4range(10,20);
+ count 
+-------
+     2
+(1 row)
+
+select count(*) from test_range_gist where ir @> 10;
+ count 
+-------
+   130
+(1 row)
+
+select count(*) from test_range_gist where ir @> int4range(10,20);
+ count 
+-------
+   111
+(1 row)
+
+select count(*) from test_range_gist where ir && int4range(10,20);
+ count 
+-------
+   158
+(1 row)
+
+select count(*) from test_range_gist where ir <@ int4range(10,50);
+ count 
+-------
+  1062
+(1 row)
+
+select count(*) from test_range_gist where ir << int4range(100,500);
+ count 
+-------
+   189
+(1 row)
+
+select count(*) from test_range_gist where ir >> int4range(100,500);
+ count 
+-------
+  3554
+(1 row)
+
+select count(*) from test_range_gist where ir &< int4range(100,500);
+ count 
+-------
+  1029
+(1 row)
+
+select count(*) from test_range_gist where ir &> int4range(100,500);
+ count 
+-------
+  4794
+(1 row)
+
+select count(*) from test_range_gist where ir -|- int4range(100,500);
+ count 
+-------
+     5
+(1 row)
+
+COMMIT;
+drop index test_range_gist_idx;
+create index test_range_gist_idx on test_range_gist using gist (ir);
+BEGIN;
+SET LOCAL enable_seqscan    = f;
+SET LOCAL enable_bitmapscan = f;
+SET LOCAL enable_indexscan  = t;
+select count(*) from test_range_gist where ir @> 'empty'::int4range;
+ count 
+-------
+  6200
+(1 row)
+
+select count(*) from test_range_gist where ir = int4range(10,20);
+ count 
+-------
+     2
+(1 row)
+
+select count(*) from test_range_gist where ir @> 10;
+ count 
+-------
+   130
+(1 row)
+
+select count(*) from test_range_gist where ir @> int4range(10,20);
+ count 
+-------
+   111
+(1 row)
+
+select count(*) from test_range_gist where ir && int4range(10,20);
+ count 
+-------
+   158
+(1 row)
+
+select count(*) from test_range_gist where ir <@ int4range(10,50);
+ count 
+-------
+  1062
+(1 row)
+
+select count(*) from test_range_gist where ir << int4range(100,500);
+ count 
+-------
+   189
+(1 row)
+
+select count(*) from test_range_gist where ir >> int4range(100,500);
+ count 
+-------
+  3554
+(1 row)
+
+select count(*) from test_range_gist where ir &< int4range(100,500);
+ count 
+-------
+  1029
+(1 row)
+
+select count(*) from test_range_gist where ir &> int4range(100,500);
+ count 
+-------
+  4794
+(1 row)
+
+select count(*) from test_range_gist where ir -|- int4range(100,500);
+ count 
+-------
+     5
+(1 row)
+
+COMMIT;
+drop table test_range_gist;
+--
+-- Btree_gist is not included by default, so to test exclusion
+-- constraints with range types, use singleton int ranges for the "="
+-- portion of the constraint.
+--
+create table test_range_excl(
+  room int4range,
+  speaker int4range,
+  during tsrange,
+  exclude using gist (room with =, during with &&),
+  exclude using gist (speaker with =, during with &&)
+);
+NOTICE:  CREATE TABLE / EXCLUDE will create implicit index "test_range_excl_room_during_excl" for table "test_range_excl"
+NOTICE:  CREATE TABLE / EXCLUDE will create implicit index "test_range_excl_speaker_during_excl" for table "test_range_excl"
+insert into test_range_excl
+  values(int4range(123), int4range(1), '[2010-01-02 10:00, 2010-01-02 11:00)');
+insert into test_range_excl
+  values(int4range(123), int4range(2), '[2010-01-02 11:00, 2010-01-02 12:00)');
+insert into test_range_excl
+  values(int4range(123), int4range(3), '[2010-01-02 10:10, 2010-01-02 11:10)');
+ERROR:  conflicting key value violates exclusion constraint "test_range_excl_room_during_excl"
+DETAIL:  Key (room, during)=([123,124), ["Sat Jan 02 10:10:00 2010","Sat Jan 02 11:10:00 2010")) conflicts with existing key (room, during)=([123,124), ["Sat Jan 02 10:00:00 2010","Sat Jan 02 11:00:00 2010")).
+insert into test_range_excl
+  values(int4range(124), int4range(3), '[2010-01-02 10:10, 2010-01-02 11:10)');
+insert into test_range_excl
+  values(int4range(125), int4range(1), '[2010-01-02 10:10, 2010-01-02 11:10)');
+ERROR:  conflicting key value violates exclusion constraint "test_range_excl_speaker_during_excl"
+DETAIL:  Key (speaker, during)=([1,2), ["Sat Jan 02 10:10:00 2010","Sat Jan 02 11:10:00 2010")) conflicts with existing key (speaker, during)=([1,2), ["Sat Jan 02 10:00:00 2010","Sat Jan 02 11:00:00 2010")).
+drop table test_range_excl;
+-- test bigint ranges
+select int8range(10000000000::int8, 20000000000::int8,'(]');
+         int8range         
+---------------------------
+ [10000000001,20000000001)
+(1 row)
+
+-- test tstz ranges
+set timezone to '-08';
+select '[2010-01-01 01:00:00 -05, 2010-01-01 02:00:00 -08)'::tstzrange;
+                            tstzrange                            
+-----------------------------------------------------------------
+ ["Thu Dec 31 22:00:00 2009 -08","Fri Jan 01 02:00:00 2010 -08")
+(1 row)
+
+-- should fail
+select '[2010-01-01 01:00:00 -08, 2010-01-01 02:00:00 -05)'::tstzrange;
+ERROR:  range lower bound must be less than or equal to range upper bound
+LINE 1: select '[2010-01-01 01:00:00 -08, 2010-01-01 02:00:00 -05)':...
+               ^
+set timezone to default;
+--
+-- Test user-defined range of floats
+--
+--should fail
+create type float8range as range (subtype=float8, subtype_diff=float4mi);
+ERROR:  function float4mi(double precision, double precision) does not exist
+--should succeed
+create type float8range as range (subtype=float8, subtype_diff=float8mi);
+select '[123.001, 5.e9)'::float8range @> 888.882::float8;
+ ?column? 
+----------
+ t
+(1 row)
+
+create table float8range_test(f8r float8range, i int);
+insert into float8range_test values(float8range(-100.00007, '1.111113e9'));
+select * from float8range_test;
+           f8r           | i 
+-------------------------+---
+ [-100.00007,1111113000) |  
+(1 row)
+
+drop table float8range_test;
+drop type float8range;
+--
+-- Test range types over domains
+--
+create domain mydomain as int4;
+create type mydomainrange as range(subtype=mydomain);
+select '[4,50)'::mydomainrange @> 7::mydomain;
+ ?column? 
+----------
+ t
+(1 row)
+
+drop type mydomainrange;
+drop domain mydomain;
+--
+-- Test domains over range types
+--
+create domain restrictedrange  as int4range check (upper(value) < 10);
+select '[4,5)'::restrictedrange @> 7;
+ ?column? 
+----------
+ f
+(1 row)
+
+select '[4,50)'::restrictedrange @> 7; -- should fail
+ERROR:  value for domain restrictedrange violates check constraint "restrictedrange_check"
+drop domain restrictedrange;
+--
+-- Test multiple range types over the same subtype
+--
+create type textrange1 as range(subtype=text, collation="C");
+create type textrange2 as range(subtype=text, collation="C");
+select textrange1('a','Z') @> 'b'::text;
+ERROR:  range lower bound must be less than or equal to range upper bound
+select textrange2('a','z') @> 'b'::text;
+ ?column? 
+----------
+ t
+(1 row)
+
+drop type textrange1;
+drop type textrange2;
+--
+-- Test out polymorphic type system
+--
+create function anyarray_anyrange_func(a anyarray, r anyrange)
+  returns anyelement as 'select $1[1] + lower($2);' language sql;
+select anyarray_anyrange_func(ARRAY[1,2], int4range(10,20));
+ anyarray_anyrange_func 
+------------------------
+                     11
+(1 row)
+
+-- should fail
+select anyarray_anyrange_func(ARRAY[1,2], numrange(10,20));
+ERROR:  function anyarray_anyrange_func(integer[], numrange) does not exist
+LINE 1: select anyarray_anyrange_func(ARRAY[1,2], numrange(10,20));
+               ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
+drop function anyarray_anyrange_func(anyarray, anyrange);
+-- should fail
+create function bogus_func(anyelement)
+  returns anyrange as 'select int4range(1,10)' language sql;
+ERROR:  cannot determine result data type
+DETAIL:  A function returning ANYRANGE must have at least one ANYRANGE argument.
+-- should fail
+create function bogus_func(int)
+  returns anyrange as 'select int4range(1,10)' language sql;
+ERROR:  cannot determine result data type
+DETAIL:  A function returning a polymorphic type must have at least one polymorphic argument.
+create function range_add_bounds(anyrange)
+  returns anyelement as 'select lower($1) + upper($1)' language sql;
+select range_add_bounds(numrange(1.0001, 123.123));
+ range_add_bounds 
+------------------
+         124.1231
+(1 row)
+
+--
+-- Arrays of ranges
+--
+select ARRAY[numrange(1.1), numrange(12.3,155.5)];
+            array             
+------------------------------
+ {"[1.1,1.1]","[12.3,155.5)"}
+(1 row)
+
+--
+-- Ranges of arrays
+--
+create type arrayrange as range (subtype=int4[]);
+select arrayrange(ARRAY[1,2], ARRAY[2,1]);
+    arrayrange     
+-------------------
+ ["{1,2}","{2,1}")
+(1 row)
+
+drop type arrayrange;
+--
+-- OUT/INOUT/TABLE functions
+--
+create function outparam_succeed(i anyrange, out r anyrange, out t text)
+  as $$ select $1, 'foo' $$ language sql;
+create function inoutparam_succeed(out i anyelement, inout r anyrange)
+  as $$ select $1, $2 $$ language sql;
+create function table_succeed(i anyelement, r anyrange) returns table(i anyelement, r anyrange)
+  as $$ select $1, $2 $$ language sql;
+-- should fail
+create function outparam_fail(i anyelement, out r anyrange, out t text)
+  as $$ select '[1,10]', 'foo' $$ language sql;
+ERROR:  cannot determine result data type
+DETAIL:  A function returning ANYRANGE must have at least one ANYRANGE argument.
+--should fail
+create function inoutparam_fail(inout i anyelement, out r anyrange)
+  as $$ select $1, '[1,10]' $$ language sql;
+ERROR:  cannot determine result data type
+DETAIL:  A function returning ANYRANGE must have at least one ANYRANGE argument.
+--should fail
+create function table_succeed(i anyelement) returns table(i anyelement, r anyrange)
+  as $$ select $1, '[1,10]' $$ language sql;
+ERROR:  cannot determine result data type
+DETAIL:  A function returning ANYRANGE must have at least one ANYRANGE argument.
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index d42b0ea045bf89b7b8a4c56eded03d7a4848682b..38c88d977ad81e8170352c0b5669449ad5906ebe 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -116,6 +116,7 @@ SELECT relname, relhasindex
  pg_opfamily             | t
  pg_pltemplate           | t
  pg_proc                 | t
+ pg_range                | t
  pg_rewrite              | t
  pg_seclabel             | t
  pg_shdepend             | t
@@ -158,7 +159,7 @@ SELECT relname, relhasindex
  timetz_tbl              | f
  tinterval_tbl           | f
  varchar_tbl             | f
-(147 rows)
+(148 rows)
 
 --
 -- another sanity check: every system catalog that has OIDs should have
diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out
index e30ecbc6feba07d49bec5c27204b45418c096ccf..7ca7a95ec66c32d782d7ddb7f4a9eabdd7a31b7e 100644
--- a/src/test/regress/expected/type_sanity.out
+++ b/src/test/regress/expected/type_sanity.out
@@ -17,7 +17,7 @@ SELECT p1.oid, p1.typname
 FROM pg_type as p1
 WHERE p1.typnamespace = 0 OR
     (p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
-    (p1.typtype not in ('b', 'c', 'd', 'e', 'p')) OR
+    (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r')) OR
     NOT p1.typisdefined OR
     (p1.typalign not in ('c', 's', 'i', 'd')) OR
     (p1.typstorage not in ('p', 'x', 'e', 'm'));
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 376f28d99a1ead3a0c2c43cfe07d4067e9f24c9f..70976d115cde16d8a3db55b00fdf0041869f5a3c 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -13,7 +13,7 @@ test: tablespace
 # ----------
 # The first group of parallel tests
 # ----------
-test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeric txid uuid enum money
+test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeric txid uuid enum money rangetypes
 
 # Depends on things setup during char, varchar and text
 test: strings
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index bb654f9c612970ef777e8cc39369a91e343f6afc..2e87d9eefd6fbb70a5603bc000ffe8335945201f 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -18,6 +18,7 @@ test: txid
 test: uuid
 test: enum
 test: money
+test: rangetypes
 test: strings
 test: numerology
 test: point
diff --git a/src/test/regress/sql/collate.linux.utf8.sql b/src/test/regress/sql/collate.linux.utf8.sql
index 73f5587b700f5c616c95007d73652e987386e242..3b7cc6cf2bcc086016cb3ae2b04ad8e8915e9bb2 100644
--- a/src/test/regress/sql/collate.linux.utf8.sql
+++ b/src/test/regress/sql/collate.linux.utf8.sql
@@ -385,3 +385,14 @@ DROP COLLATION test0 CASCADE;
 
 DROP TABLE collate_dep_test1, collate_dep_test4t;
 DROP TYPE collate_dep_test2;
+
+-- test range types and collations
+
+create type textrange_c as range(subtype=text, collation="C");
+create type textrange_en_us as range(subtype=text, collation="en_US");
+
+select textrange_c('A','Z') @> 'b'::text;
+select textrange_en_us('A','Z') @> 'b'::text;
+
+drop type textrange_c;
+drop type textrange_en_us;
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index 65ae868d989388e1cc28281ed4b49419764fe251..7f936c815474205dc68cec8d5db96f786aadae9f 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -133,7 +133,9 @@ WHERE p1.oid != p2.oid AND
     p1.prosrc = p2.prosrc AND
     p1.prolang = 12 AND p2.prolang = 12 AND
     NOT p1.proisagg AND NOT p2.proisagg AND
-    (p1.prorettype < p2.prorettype)
+    (p1.prorettype < p2.prorettype) AND
+    -- range constructor functions are shared by all range types.
+    NOT p1.prosrc LIKE 'range_constructor%'
 ORDER BY 1, 2;
 
 SELECT DISTINCT p1.proargtypes[0], p2.proargtypes[0]
@@ -142,7 +144,9 @@ WHERE p1.oid != p2.oid AND
     p1.prosrc = p2.prosrc AND
     p1.prolang = 12 AND p2.prolang = 12 AND
     NOT p1.proisagg AND NOT p2.proisagg AND
-    (p1.proargtypes[0] < p2.proargtypes[0])
+    (p1.proargtypes[0] < p2.proargtypes[0]) AND
+    -- range constructor functions are shared by all range types.
+    NOT p1.prosrc LIKE 'range_constructor%'
 ORDER BY 1, 2;
 
 SELECT DISTINCT p1.proargtypes[1], p2.proargtypes[1]
@@ -151,7 +155,9 @@ WHERE p1.oid != p2.oid AND
     p1.prosrc = p2.prosrc AND
     p1.prolang = 12 AND p2.prolang = 12 AND
     NOT p1.proisagg AND NOT p2.proisagg AND
-    (p1.proargtypes[1] < p2.proargtypes[1])
+    (p1.proargtypes[1] < p2.proargtypes[1]) AND
+    -- range constructor functions are shared by all range types.
+    NOT p1.prosrc LIKE 'range_constructor%'
 ORDER BY 1, 2;
 
 SELECT DISTINCT p1.proargtypes[2], p2.proargtypes[2]
diff --git a/src/test/regress/sql/plpgsql.sql b/src/test/regress/sql/plpgsql.sql
index b47c2de312a102a1342f54c5101be95edfa4d3b2..2906943f06fbac2a777960b3cbeb0b5303d40938 100644
--- a/src/test/regress/sql/plpgsql.sql
+++ b/src/test/regress/sql/plpgsql.sql
@@ -3600,3 +3600,13 @@ select testoa(1,2,1); -- fail at update
 
 drop function arrayassign1();
 drop function testoa(x1 int, x2 int, x3 int);
+
+-- Test resolve_polymorphic_argtypes() codepath. It is only taken when
+-- a function is invoked from a different backend from where it's defined,
+-- so we create the a function with polymorphic argument, reconnect, and
+-- and then call it.
+create function rangetypes_plpgsql(out a anyelement, b anyrange, c anyarray)
+  language plpgsql as
+  $$ begin a := upper(b) + c[1]; return; end; $$;
+\c -
+select rangetypes_plpgsql(int4range(1,10),ARRAY[2,20]);
diff --git a/src/test/regress/sql/rangetypes.sql b/src/test/regress/sql/rangetypes.sql
new file mode 100644
index 0000000000000000000000000000000000000000..44885c870cfee3d8977fd9a8b3165146641b1c85
--- /dev/null
+++ b/src/test/regress/sql/rangetypes.sql
@@ -0,0 +1,371 @@
+
+--
+-- test parser
+--
+
+create type textrange as range (subtype=text, collation="C");
+
+-- negative tests; should fail
+select ''::textrange;
+select '-[a,z)'::textrange;
+select '[a,z) - '::textrange;
+select '(",a)'::textrange;
+select '(,,a)'::textrange;
+select '(),a)'::textrange;
+select '(a,))'::textrange;
+select '(],a)'::textrange;
+select '(a,])'::textrange;
+
+-- should succeed
+select '  empty  '::textrange;
+select ' ( empty, empty )  '::textrange;
+select ' ( " a " " a ", " z " " z " )  '::textrange;
+select '(,z)'::textrange;
+select '(a,)'::textrange;
+select '[,z]'::textrange;
+select '[a,]'::textrange;
+select '( , )'::textrange;
+select '("","")'::textrange;
+select '["",""]'::textrange;
+select '(",",",")'::textrange;
+select '("\\","\\")'::textrange
+select '(\\,a)'::textrange;
+select '((,z)'::textrange;
+select '([,z)'::textrange;
+select '(!,()'::textrange;
+select '(!,[)'::textrange;
+
+drop type textrange;
+
+--
+-- create some test data and test the operators
+--
+
+CREATE TABLE numrange_test (nr NUMRANGE);
+create index numrange_test_btree on numrange_test(nr);
+SET enable_seqscan = f;
+
+INSERT INTO numrange_test VALUES('[,)');
+INSERT INTO numrange_test VALUES('[3,]');
+INSERT INTO numrange_test VALUES('[, 5)');
+INSERT INTO numrange_test VALUES(numrange(1.1, 2.2));
+INSERT INTO numrange_test VALUES('empty');
+INSERT INTO numrange_test VALUES(numrange(1.7));
+
+SELECT isempty(nr) FROM numrange_test;
+SELECT lower_inc(nr), lower(nr), upper(nr), upper_inc(nr) FROM numrange_test
+  WHERE NOT isempty(nr) AND NOT lower_inf(nr) AND NOT upper_inf(nr);
+
+SELECT * FROM numrange_test WHERE contains(nr, numrange(1.9,1.91));
+SELECT * FROM numrange_test WHERE nr @> numrange(1.0,10000.1);
+SELECT * FROM numrange_test WHERE contained_by(numrange(-1e7,-10000.1), nr);
+SELECT * FROM numrange_test WHERE 1.9 <@ nr;
+SELECT * FROM numrange_test WHERE nr = 'empty';
+SELECT * FROM numrange_test WHERE range_eq(nr, '(1.1, 2.2)');
+SELECT * FROM numrange_test WHERE nr = '[1.1, 2.2)';
+
+select numrange(2.0, 1.0);
+
+select numrange(2.0, 3.0) -|- numrange(3.0, 4.0);
+select adjacent(numrange(2.0, 3.0), numrange(3.1, 4.0));
+select numrange(2.0, 3.0, '[]') -|- numrange(3.0, 4.0, '()');
+select numrange(1.0, 2.0) -|- numrange(2.0, 3.0,'[]');
+select adjacent(numrange(2.0, 3.0, '(]'), numrange(1.0, 2.0, '(]'));
+
+select numrange(1.1, 3.3) <@ numrange(0.1,10.1);
+select numrange(0.1, 10.1) <@ numrange(1.1,3.3);
+
+select numrange(1.1, 2.2) - numrange(2.0, 3.0);
+select numrange(1.1, 2.2) - numrange(2.2, 3.0);
+select numrange(1.1, 2.2,'[]') - numrange(2.0, 3.0);
+select minus(numrange(10.1,12.2,'[]'), numrange(110.0,120.2,'(]'));
+select minus(numrange(10.1,12.2,'[]'), numrange(0.0,120.2,'(]'));
+
+select numrange(4.5, 5.5, '[]') && numrange(5.5, 6.5);
+select numrange(1.0, 2.0) << numrange(3.0, 4.0);
+select numrange(1.0, 2.0) >> numrange(3.0, 4.0);
+select numrange(3.0, 70.0) &< numrange(6.6, 100.0);
+
+select numrange(1.1, 2.2) < numrange(1.0, 200.2);
+select numrange(1.1, 2.2) < numrange(1.1, 1.2);
+
+select numrange(1.0, 2.0) + numrange(2.0, 3.0);
+select numrange(1.0, 2.0) + numrange(1.5, 3.0);
+select numrange(1.0, 2.0) + numrange(2.5, 3.0);
+
+select numrange(1.0, 2.0) * numrange(2.0, 3.0);
+select numrange(1.0, 2.0) * numrange(1.5, 3.0);
+select numrange(1.0, 2.0) * numrange(2.5, 3.0);
+
+select * from numrange_test where nr < numrange(-1000.0, -1000.0,'[]');
+select * from numrange_test where nr < numrange(0.0, 1.0,'[]');
+select * from numrange_test where nr < numrange(1000.0, 1001.0,'[]');
+select * from numrange_test where nr > numrange(-1001.0, -1000.0,'[]');
+select * from numrange_test where nr > numrange(0.0, 1.0,'[]');
+select * from numrange_test where nr > numrange(1000.0, 1000.0,'[]');
+
+create table numrange_test2(nr numrange);
+create index numrange_test2_hash_idx on numrange_test2 (nr);
+INSERT INTO numrange_test2 VALUES('[, 5)');
+INSERT INTO numrange_test2 VALUES(numrange(1.1, 2.2));
+INSERT INTO numrange_test2 VALUES(numrange(1.1, 2.2));
+INSERT INTO numrange_test2 VALUES(numrange(1.1, 2.2,'()'));
+INSERT INTO numrange_test2 VALUES('empty');
+
+select * from numrange_test2 where nr = 'empty'::numrange;
+select * from numrange_test2 where nr = numrange(1.1, 2.2);
+select * from numrange_test2 where nr = numrange(1.1, 2.3);
+
+set enable_nestloop=t;
+set enable_hashjoin=f;
+set enable_mergejoin=f;
+select * from numrange_test natural join numrange_test2 order by nr;
+set enable_nestloop=f;
+set enable_hashjoin=t;
+set enable_mergejoin=f;
+select * from numrange_test natural join numrange_test2 order by nr;
+set enable_nestloop=f;
+set enable_hashjoin=f;
+set enable_mergejoin=t;
+select * from numrange_test natural join numrange_test2 order by nr;
+
+set enable_nestloop to default;
+set enable_hashjoin to default;
+set enable_mergejoin to default;
+SET enable_seqscan TO DEFAULT;
+DROP TABLE numrange_test;
+DROP TABLE numrange_test2;
+
+-- test canonical form for int4range
+select int4range(1,10,'[]');
+select int4range(1,10,'[)');
+select int4range(1,10,'(]');
+select int4range(1,10,'[]');
+
+-- test canonical form for daterange
+select daterange('2000-01-10'::date, '2000-01-20'::date,'[]');
+select daterange('2000-01-10'::date, '2000-01-20'::date,'[)');
+select daterange('2000-01-10'::date, '2000-01-20'::date,'(]');
+select daterange('2000-01-10'::date, '2000-01-20'::date,'[]');
+
+create table test_range_gist(ir int4range);
+create index test_range_gist_idx on test_range_gist using gist (ir);
+
+insert into test_range_gist select int4range(g, g+10) from generate_series(1,2000) g;
+insert into test_range_gist select 'empty'::int4range from generate_series(1,500) g;
+insert into test_range_gist select int4range(g, g+10000) from generate_series(1,1000) g;
+insert into test_range_gist select 'empty'::int4range from generate_series(1,500) g;
+insert into test_range_gist select int4range(NULL,g*10,'(]') from generate_series(1,100) g;
+insert into test_range_gist select int4range(g*10,NULL,'(]') from generate_series(1,100) g;
+insert into test_range_gist select int4range(g, g+10) from generate_series(1,2000) g;
+
+BEGIN;
+SET LOCAL enable_seqscan    = t;
+SET LOCAL enable_bitmapscan = f;
+SET LOCAL enable_indexscan  = f;
+
+select count(*) from test_range_gist where ir @> 'empty'::int4range;
+select count(*) from test_range_gist where ir = int4range(10,20);
+select count(*) from test_range_gist where ir @> 10;
+select count(*) from test_range_gist where ir @> int4range(10,20);
+select count(*) from test_range_gist where ir && int4range(10,20);
+select count(*) from test_range_gist where ir <@ int4range(10,50);
+select count(*) from (select * from test_range_gist where not isempty(ir)) s where ir << int4range(100,500);
+select count(*) from (select * from test_range_gist where not isempty(ir)) s where ir >> int4range(100,500);
+select count(*) from (select * from test_range_gist where not isempty(ir)) s where ir &< int4range(100,500);
+select count(*) from (select * from test_range_gist where not isempty(ir)) s where ir &> int4range(100,500);
+select count(*) from (select * from test_range_gist where not isempty(ir)) s where ir -|- int4range(100,500);
+COMMIT;
+
+BEGIN;
+SET LOCAL enable_seqscan    = f;
+SET LOCAL enable_bitmapscan = f;
+SET LOCAL enable_indexscan  = t;
+
+select count(*) from test_range_gist where ir @> 'empty'::int4range;
+select count(*) from test_range_gist where ir = int4range(10,20);
+select count(*) from test_range_gist where ir @> 10;
+select count(*) from test_range_gist where ir @> int4range(10,20);
+select count(*) from test_range_gist where ir && int4range(10,20);
+select count(*) from test_range_gist where ir <@ int4range(10,50);
+select count(*) from test_range_gist where ir << int4range(100,500);
+select count(*) from test_range_gist where ir >> int4range(100,500);
+select count(*) from test_range_gist where ir &< int4range(100,500);
+select count(*) from test_range_gist where ir &> int4range(100,500);
+select count(*) from test_range_gist where ir -|- int4range(100,500);
+COMMIT;
+
+drop index test_range_gist_idx;
+create index test_range_gist_idx on test_range_gist using gist (ir);
+
+BEGIN;
+SET LOCAL enable_seqscan    = f;
+SET LOCAL enable_bitmapscan = f;
+SET LOCAL enable_indexscan  = t;
+
+select count(*) from test_range_gist where ir @> 'empty'::int4range;
+select count(*) from test_range_gist where ir = int4range(10,20);
+select count(*) from test_range_gist where ir @> 10;
+select count(*) from test_range_gist where ir @> int4range(10,20);
+select count(*) from test_range_gist where ir && int4range(10,20);
+select count(*) from test_range_gist where ir <@ int4range(10,50);
+select count(*) from test_range_gist where ir << int4range(100,500);
+select count(*) from test_range_gist where ir >> int4range(100,500);
+select count(*) from test_range_gist where ir &< int4range(100,500);
+select count(*) from test_range_gist where ir &> int4range(100,500);
+select count(*) from test_range_gist where ir -|- int4range(100,500);
+COMMIT;
+
+drop table test_range_gist;
+
+--
+-- Btree_gist is not included by default, so to test exclusion
+-- constraints with range types, use singleton int ranges for the "="
+-- portion of the constraint.
+--
+
+create table test_range_excl(
+  room int4range,
+  speaker int4range,
+  during tsrange,
+  exclude using gist (room with =, during with &&),
+  exclude using gist (speaker with =, during with &&)
+);
+
+insert into test_range_excl
+  values(int4range(123), int4range(1), '[2010-01-02 10:00, 2010-01-02 11:00)');
+insert into test_range_excl
+  values(int4range(123), int4range(2), '[2010-01-02 11:00, 2010-01-02 12:00)');
+insert into test_range_excl
+  values(int4range(123), int4range(3), '[2010-01-02 10:10, 2010-01-02 11:10)');
+insert into test_range_excl
+  values(int4range(124), int4range(3), '[2010-01-02 10:10, 2010-01-02 11:10)');
+insert into test_range_excl
+  values(int4range(125), int4range(1), '[2010-01-02 10:10, 2010-01-02 11:10)');
+
+drop table test_range_excl;
+
+-- test bigint ranges
+select int8range(10000000000::int8, 20000000000::int8,'(]');
+-- test tstz ranges
+set timezone to '-08';
+select '[2010-01-01 01:00:00 -05, 2010-01-01 02:00:00 -08)'::tstzrange;
+-- should fail
+select '[2010-01-01 01:00:00 -08, 2010-01-01 02:00:00 -05)'::tstzrange;
+set timezone to default;
+
+--
+-- Test user-defined range of floats
+--
+
+--should fail
+create type float8range as range (subtype=float8, subtype_diff=float4mi);
+
+--should succeed
+create type float8range as range (subtype=float8, subtype_diff=float8mi);
+select '[123.001, 5.e9)'::float8range @> 888.882::float8;
+create table float8range_test(f8r float8range, i int);
+insert into float8range_test values(float8range(-100.00007, '1.111113e9'));
+select * from float8range_test;
+drop table float8range_test;
+drop type float8range;
+
+--
+-- Test range types over domains
+--
+
+create domain mydomain as int4;
+create type mydomainrange as range(subtype=mydomain);
+select '[4,50)'::mydomainrange @> 7::mydomain;
+drop type mydomainrange;
+drop domain mydomain;
+
+--
+-- Test domains over range types
+--
+
+create domain restrictedrange  as int4range check (upper(value) < 10);
+select '[4,5)'::restrictedrange @> 7;
+select '[4,50)'::restrictedrange @> 7; -- should fail
+drop domain restrictedrange;
+
+--
+-- Test multiple range types over the same subtype
+--
+
+create type textrange1 as range(subtype=text, collation="C");
+create type textrange2 as range(subtype=text, collation="C");
+
+select textrange1('a','Z') @> 'b'::text;
+select textrange2('a','z') @> 'b'::text;
+
+drop type textrange1;
+drop type textrange2;
+
+--
+-- Test out polymorphic type system
+--
+
+create function anyarray_anyrange_func(a anyarray, r anyrange)
+  returns anyelement as 'select $1[1] + lower($2);' language sql;
+
+select anyarray_anyrange_func(ARRAY[1,2], int4range(10,20));
+
+-- should fail
+select anyarray_anyrange_func(ARRAY[1,2], numrange(10,20));
+
+drop function anyarray_anyrange_func(anyarray, anyrange);
+
+-- should fail
+create function bogus_func(anyelement)
+  returns anyrange as 'select int4range(1,10)' language sql;
+
+-- should fail
+create function bogus_func(int)
+  returns anyrange as 'select int4range(1,10)' language sql;
+
+create function range_add_bounds(anyrange)
+  returns anyelement as 'select lower($1) + upper($1)' language sql;
+
+select range_add_bounds(numrange(1.0001, 123.123));
+
+--
+-- Arrays of ranges
+--
+
+select ARRAY[numrange(1.1), numrange(12.3,155.5)];
+
+--
+-- Ranges of arrays
+--
+
+create type arrayrange as range (subtype=int4[]);
+
+select arrayrange(ARRAY[1,2], ARRAY[2,1]);
+
+drop type arrayrange;
+
+--
+-- OUT/INOUT/TABLE functions
+--
+
+create function outparam_succeed(i anyrange, out r anyrange, out t text)
+  as $$ select $1, 'foo' $$ language sql;
+
+create function inoutparam_succeed(out i anyelement, inout r anyrange)
+  as $$ select $1, $2 $$ language sql;
+
+create function table_succeed(i anyelement, r anyrange) returns table(i anyelement, r anyrange)
+  as $$ select $1, $2 $$ language sql;
+
+-- should fail
+create function outparam_fail(i anyelement, out r anyrange, out t text)
+  as $$ select '[1,10]', 'foo' $$ language sql;
+
+--should fail
+create function inoutparam_fail(inout i anyelement, out r anyrange)
+  as $$ select $1, '[1,10]' $$ language sql;
+
+--should fail
+create function table_succeed(i anyelement) returns table(i anyelement, r anyrange)
+  as $$ select $1, '[1,10]' $$ language sql;
diff --git a/src/test/regress/sql/type_sanity.sql b/src/test/regress/sql/type_sanity.sql
index fa6dd75f07fd4066f12fad76fb651c07b9c35e9d..1638861bc1dd8e270739a7c1191dfa11d2059d9e 100644
--- a/src/test/regress/sql/type_sanity.sql
+++ b/src/test/regress/sql/type_sanity.sql
@@ -20,7 +20,7 @@ SELECT p1.oid, p1.typname
 FROM pg_type as p1
 WHERE p1.typnamespace = 0 OR
     (p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
-    (p1.typtype not in ('b', 'c', 'd', 'e', 'p')) OR
+    (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r')) OR
     NOT p1.typisdefined OR
     (p1.typalign not in ('c', 's', 'i', 'd')) OR
     (p1.typstorage not in ('p', 'x', 'e', 'm'));