Support range data types.
authorHeikki Linnakangas
Thu, 3 Nov 2011 11:16:28 +0000 (13:16 +0200)
committerHeikki Linnakangas
Thu, 3 Nov 2011 11:42:15 +0000 (13:42 +0200)
Selectivity estimation functions are missing for some range type operators,
which is a TODO.

Jeff Davis

58 files changed:
doc/src/sgml/catalogs.sgml
doc/src/sgml/datatype.sgml
doc/src/sgml/extend.sgml
doc/src/sgml/filelist.sgml
doc/src/sgml/func.sgml
doc/src/sgml/plpgsql.sgml
doc/src/sgml/rangetypes.sgml [new file with mode: 0644]
doc/src/sgml/ref/create_type.sgml
doc/src/sgml/xfunc.sgml
src/backend/catalog/Makefile
src/backend/catalog/pg_proc.c
src/backend/catalog/pg_range.c [new file with mode: 0644]
src/backend/commands/typecmds.c
src/backend/executor/functions.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/parser/gram.y
src/backend/parser/parse_coerce.c
src/backend/tcop/utility.c
src/backend/utils/adt/Makefile
src/backend/utils/adt/date.c
src/backend/utils/adt/pseudotypes.c
src/backend/utils/adt/rangetypes.c [new file with mode: 0644]
src/backend/utils/adt/rangetypes_gist.c [new file with mode: 0644]
src/backend/utils/cache/lsyscache.c
src/backend/utils/cache/syscache.c
src/backend/utils/fmgr/funcapi.c
src/bin/pg_dump/pg_dump.c
src/include/catalog/catversion.h
src/include/catalog/indexing.h
src/include/catalog/pg_amop.h
src/include/catalog/pg_amproc.h
src/include/catalog/pg_opclass.h
src/include/catalog/pg_operator.h
src/include/catalog/pg_opfamily.h
src/include/catalog/pg_proc.h
src/include/catalog/pg_range.h [new file with mode: 0644]
src/include/catalog/pg_type.h
src/include/commands/typecmds.h
src/include/nodes/nodes.h
src/include/nodes/parsenodes.h
src/include/utils/lsyscache.h
src/include/utils/rangetypes.h [new file with mode: 0644]
src/include/utils/syscache.h
src/pl/plpgsql/src/pl_comp.c
src/test/regress/expected/collate.linux.utf8.out
src/test/regress/expected/opr_sanity.out
src/test/regress/expected/plpgsql.out
src/test/regress/expected/rangetypes.out [new file with mode: 0644]
src/test/regress/expected/sanity_check.out
src/test/regress/expected/type_sanity.out
src/test/regress/parallel_schedule
src/test/regress/serial_schedule
src/test/regress/sql/collate.linux.utf8.sql
src/test/regress/sql/opr_sanity.sql
src/test/regress/sql/plpgsql.sql
src/test/regress/sql/rangetypes.sql [new file with mode: 0644]
src/test/regress/sql/type_sanity.sql

index cfecaa6931a9352f909966e021555b92e984fc6f..2063812942faa4b95b576703f4a9b3be3d428a90 100644 (file)
       functions and procedures
      
 
+     
+      pg_range
+      information about range types
+     
+
      
       pg_rewrite
       query rewrite rules
 
  
 
+  <structname>pg_range</structname>
+
+  
+   pg_range
+  
+
+  
+   The catalog pg_range stores information about range types.
+  
+
+  
+   <structname>pg_range</> Columns
+
+   
+    
+     
+      Name
+      Type
+      References
+      Description
+     
+    
+
+    
+     
+      rngtypid
+      oid
+      pg_type.oid
+      The type that is a range type
+     
+
+     
+      rngsubtype
+      oid
+      pg_type.oid
+      Subtype of this range type, e.g. integer is the subtype of int4range
+     
+
+     
+      rngcollation
+      oid
+      pg_collation.oid
+      The collation used when comparing range boundaries
+     
+
+     
+      rngsubopc
+      oid
+      pg_opclass.oid
+      The operator class used when comparing range boundaries
+     
+
+     
+      rngcanonical
+      regproc
+      pg_proc.oid
+      A function to convert a range into its canonical form
+     
+
+     
+      rngsubdiff
+      regproc
+      pg_proc.oid
+      A function to return the distance between two lower and upper bound, as a double precision. Used for GiST support
+     
+    
+   
+  
+
+
  
   <structname>pg_rewrite</structname>
 
index e7b3098f28ffb1b7f66c64a59fbba2c7a9a28222..fe59a1c7763ee970508bbd2d09f5d5dcb28cfd1f 100644 (file)
@@ -4173,6 +4173,8 @@ SET xmloption TO { DOCUMENT | CONTENT };
 
   &rowtypes;
 
+  &rangetypes;
+
   
    Object Identifier Types
 
@@ -4443,6 +4445,10 @@ SELECT * FROM pg_attribute
     anyenum
    
 
+   
+    anyrange
+   
+
    
     void
    
@@ -4519,6 +4525,13 @@ SELECT * FROM pg_attribute
         ).
        
 
+       
+        anyrange
+        Indicates that a function accepts any range data type
+        (see  and
+        ).
+       
+
        
         anynonarray
         Indicates that a function accepts any non-array data type
@@ -4583,7 +4596,8 @@ SELECT * FROM pg_attribute
     only void and record as a result type (plus
     trigger when the function is used as a trigger).  Some also
     support polymorphic functions using the types anyarray,
-    anyelement, anyenum, and anynonarray.
+    anyelement, anyenum, anyrange, and
+    anynonarray.
    
 
    
index 7079db3ed3f7be7f55bef76b5fae4b9958f4c4aa..f3850b391e0e5244a19a567e8c172bb3e27b297d 100644 (file)
    
 
     
-     Four pseudo-types of special interest are anyelement,
-     anyarray, anynonarray, and anyenum,
-     which are collectively called polymorphic types.
-     Any function declared using these types is said to be
-     a 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 anyelement,
+     anyarray, anynonarray, anyenum,
+     and anyrange, which are collectively
+     called polymorphic types.  Any function declared
+     using these types is said to be a 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.
     
 
     
      anyelement, the actual array type in the
      anyarray positions must be an array whose elements are
      the same type appearing in the anyelement positions.
+     Similarly, if there are positions declared anyrange
+     and others declared
+     anyelement, the actual range type in the
+     anyrange positions must be a range whose subtype is
+     the same type appearing in the anyelement positions.
      anynonarray is treated exactly the same as anyelement,
      but adds the additional constraint that the actual type must not be
      an array type.
index ed39e0b66126a7a747103ea30787afee51253e6e..fb69415f8000d65cb5757e3c0f788ce82f5f27cc 100644 (file)
@@ -25,6 +25,7 @@
 
 
 
+
 
 
 
index 2b8298c3e0cbadf73c9bb2a221f2d2f1f8b77999..f81bb9db9770422bf7dcb5c032ef2c1fdb5a51a6 100644 (file)
@@ -10457,6 +10457,310 @@ SELECT NULLIF(value, '(none)') ...
    
   
 
+  Range Functions and Operators
+
+  
+    shows the operators
+   available for range types.
+  
+
+    
+     Range Operators
+     
+      
+       
+        Operator
+        Description
+        Example
+        Result
+       
+      
+      
+       
+         = 
+        equal
+        int4range(1,5) = '[1,4]'::int4range
+        t
+       
+
+       
+         <> 
+        not equal
+        numrange(1.1,2.2) <> numrange(1.1,2.3)
+        t
+       
+
+       
+         < 
+        less than
+        int4range(1,10) < int4range(2,3)
+        t
+       
+
+       
+         > 
+        greater than
+        int4range(1,10) > int4range(1,5)
+        t
+       
+
+       
+         <= 
+        less than or equal
+        numrange(1.1,2.2) <= numrange(1.1,2.2)
+        t
+       
+
+       
+         >= 
+        greater than or equal
+        numrange(1.1,2.2) >= numrange(1.1,2.0)
+        t
+       
+
+       
+         @> 
+        contains
+        '[2011-01-01,2011-03-01)'::tsrange @> '2011-01-10'::timestamp
+        t
+       
+
+       
+         <@ 
+        is contained by
+        int4range(2,4) <@ int4range(1,7)
+        t
+       
+
+       
+         && 
+        overlap (have points in common)
+        int8range(3,7) && int8range(4,12)
+        t
+       
+
+       
+         << 
+        strictly left of
+        int8range(1,10) << int8range(100,110)
+        t
+       
+
+       
+         >> 
+        strictly right of
+        int8range(50,60) >> int8range(20,30)
+        t
+       
+
+       
+         &< 
+        Does not extend to the right of?
+        int8range(1,20) &< int8range(18,20)
+        t
+       
+
+       
+         &> 
+        Does not extend to the left of?
+        int8range(7,20) &> int8range(5,10)
+        t
+       
+
+       
+         -|- 
+        adjacent?
+        numrange(1.1,2.2) -|- numrange(2.2,3.3)
+        t
+       
+
+       
+         + 
+        Union
+        numrange(5,15) + numrange(10,20)
+        [5,20)
+       
+
+       
+         - 
+        Difference
+        int8range(5,15) - int8range(10,20)
+        [5,10)
+       
+
+       
+         * 
+        Intersection
+        int8range(5,15) * int8range(10,20)
+        [10,15)
+       
+
+       
+         !? 
+        Is empty?
+        'empty'::int4range !?
+        t
+       
+
+       
+         ? 
+        Is non-empty?
+        numrange(1.0,2.0)?
+        t
+       
+      
+     
+    
+
+  
+   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.
+  
+
+  
+   See  for more details about range operator
+   behavior.
+  
+
+  
+    shows the functions
+   available for use with range types. See 
+   for more information  and examples of the use of these functions.
+  
+
+  
+    lower
+  
+  
+    upper
+  
+  
+    empty
+  
+  
+    non_empty
+  
+  
+    lower_inc
+  
+  
+    upper_inc
+  
+  
+    lower_inf
+  
+  
+    upper_inf
+  
+
+    
+     Range Functions
+     
+      
+       
+        Function
+        Return Type
+        Description
+        Example
+        Result
+       
+      
+      
+       
+        
+         
+          lower(anyrange)
+         
+        
+        anyrange
+        lower bound of range
+        lower(numrange(1.1,2.2))
+        1.1
+       
+       
+        
+         
+          upper(anyrange)
+         
+        
+        anyrange
+        upper bound of range
+        upper(numrange(1.1,2.2))
+        2.2
+       
+       
+        
+         
+          empty(anyrange)
+         
+        
+        anyrange
+        is the range empty?
+        empty(numrange(1.1,2.2))
+        false
+       
+       
+        
+         
+          non_empty(anyrange)
+         
+        
+        anyrange
+        is the range non-empty?
+        non_empty(numrange(1.1,2.2))
+        true
+       
+       
+        
+         
+          lower_inc(anyrange)
+         
+        
+        anyrange
+        is the lower bound of the range inclusive?
+        lower_inc(numrange(1.1,2.2))
+        true
+       
+       
+        
+         
+          upper_inc(anyrange)
+         
+        
+        anyrange
+        is the upper bound of the range inclusive?
+        upper_inc(numrange(1.1,2.2))
+        false
+       
+       
+        
+         
+          lower_inf(anyrange)
+         
+        
+        anyrange
+        is the lower bound of the range infinite?
+        lower_inf('(,)'::daterange)
+        true
+       
+       
+        
+         
+          upper_inf(anyrange)
+         
+        
+        anyrange
+        is the upper bound of the range infinite?
+        upper_inf('(,)'::daterange)
+        true
+       
+      
+     
+    
+  
+
  
   Aggregate Functions
 
index 19c15ad26fc3d204b52b007babbcfdbaadb075c7..f33cef55ed0e81c6b8e85df718bac143006587dd 100644 (file)
      PL/pgSQL functions can also be declared to accept
      and return the polymorphic types
      anyelementanyarrayanynonarray,
-     and anyenum>.  The actual
+     anyenum, and anyrange>.  The actual
      data types handled by a polymorphic function can vary from call to
      call, as discussed in .
      An example is shown in .
@@ -500,8 +500,8 @@ $$ LANGUAGE plpgsql;
      
       When the return type of a PL/pgSQL
       function is declared as a polymorphic type (anyelement,
-      anyarrayanynonarrayor anyenum),
-      a special parameter $0
+      anyarrayanynonarrayanyenum,
+      or anyrange), a special parameter $0
       is created.  Its data type is the actual return type of the function,
       as deduced from the actual input types (see 
       linkend="extend-types-polymorphic">).
diff --git a/doc/src/sgml/rangetypes.sgml b/doc/src/sgml/rangetypes.sgml
new file mode 100644 (file)
index 0000000..fc5896d
--- /dev/null
@@ -0,0 +1,373 @@
+
+
+
Range Types
+
+  range type
+
+  Range types are data types representing a range of values over some
+  sub-type with a total order. For instance, ranges
+  of timestamp might be used to represent the ranges of
+  time that a meeting room is reserved. In this case the data type
+  is tsrange (short for "timestamp range"),
+  and timestamp is the sub-type with a total order.
+
+  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.
+
+  Built-in Range Types
+  PostgreSQL comes with the following built-in range types:
+  
+    
+      
+       INT4RANGE -- Range of INTEGER. This is a discrete range type, see .
+      
+    
+    
+      
+       INT8RANGE -- Range of BIGINT. This is a discrete range type, see .
+      
+    
+    
+      
+       NUMRANGE -- Range of NUMERIC.
+      
+    
+    
+      
+       TSRANGE -- Range of TIMESTAMP WITHOUT TIME ZONE.
+      
+    
+    
+      
+       TSTZRANGE -- Range of TIMESTAMP WITH TIME ZONE.
+      
+    
+    
+      
+       DATERANGE -- Range of DATE. This is a discrete range type, see .
+      
+    
+  
+  In addition, you can define your own; see  for more information.
+
+  Examples
+  
+
+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)? ;
+
+
+
+  See 
+  and  for complete lists of
+  functions and operators on range types.
+  
+
+  Inclusive and Exclusive Bounds
+  
+  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.
+  
+  
+  An inclusive lower bound is represented by [
+  while an exclusive lower bound is represented
+  by ( (see 
+  and  below). Likewise, an inclusive
+  upper bound is represented by ], while an
+  exclusive upper bound is represented by ).
+  
+  
+  Functions lower_inc
+  and upper_inc test the inclusivity of the lower
+  and upper bounds of a range, respectively.
+  
+
+  Infinite (unbounded) Ranges
+  
+  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.
+  
+  
+  Functions lower_inf
+  and upper_inf test the range for infinite lower
+  and upper bounds of a range, respectively.
+  
+
+  Input/Output
+  
+  The input follows one of the following patterns:
+
+(lower-bound,upper-bound)
+(lower-bound,upper-bound]
+[lower-bound,upper-bound)
+[lower-bound,upper-bound]
+empty
+
+  Notice that the final pattern is empty, which
+  represents an empty range (a range that contains no points).
+  
+  
+  The lower-bound may be either a string
+  that is valid input for the sub-type, or omitted (to indicate no
+  lower bound); and upper-bound may be
+  either a string that is valid input for the sub-type, or omitted (to
+  indicate no upper bound).
+  
+  
+  Either the lower-bound or
+  the upper-bound may be quoted
+  using "" (double quotation marks), which will allow
+  special characters such as ",". Within quotation
+  marks, "\" (backslash) serves as an escape
+  character.
+  
+  
+  The choice between the other input formats affects the inclusivity
+  of the bounds. See .
+  
+  
+  Examples:
+
+-- 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]'
+
+  
+
+  Constructing Ranges
+  
+   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 [ ) form; and the three-argument form
+   constructs a range in a form specified by the third argument. For
+   example:
+
+-- Three-argument form: lower bound, upper bound, and third argument indicating
+-- inclusivity/exclusivity of bounds (if omitted, defaults to '[)').
+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 .
+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);
+
+  
+
+  Discrete Range Types
+  
+  Discrete ranges are those that have a
+  defined canonical function. Loosely speaking, a
+  discrete range has a sub-type with a well-defined "step";
+  e.g. INTEGER or DATE.
+  
+  
+  The canonical function should take an input range
+  value, and return an equal range value that may have a different
+  formatting. For instance, the integer range [1,
+  7] could be represented by the equal integer
+  range [1, 8). The two values are equal because
+  there are no points within the integer domain
+  between 7 and 8, so not
+  including the end point 8 is the same as
+  including the end point 7. The canonical output
+  for two values that are equal, like [1, 7]
+  and [1, 8), 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. [1, 7] and [1, 8)) will
+  always be treated as unequal.
+  
+  
+  For types such as NUMRANGE, this is not possible,
+  because there are always points in between two
+  distinct NUMERIC values.
+  
+  
+  The built-in range
+  types INT4RANGEINT8RANGE,
+  and DATERNAGE all use a canonical form that includes
+  the lower bound and excludes the upper bound; that is, [
+  ). User-defined ranges can use other conventions, however.
+  
+
+  Defining New Range Types
+  
+  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 FLOAT (or, if the
+  subtype itself is a user-defined type).
+  
+  
+  For example: to define a new range type of sub-type DOUBLE PRECISION:
+
+CREATE TYPE FLOATRANGE AS RANGE (
+  SUBTYPE = DOUBLE PRECISION
+);
+
+SELECT '[1.234, 5.678]'::floatrange;
+
+  Because DOUBLE PRECISION has no meaningful "step", we
+  do not define a canonical
+  function. See  for more
+  information.
+  
+  
+  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.
+  
+
+  
+    range type
+    gist
+  
+  Indexing
+  
+   GiST indexes can be applied to a table containing a range type. For instance:
+
+CREATE INDEX reservation_idx ON reservation USING gist (during);
+
+  This index may speed up queries
+  involving &&
+  (overlaps), @> (contains), and all the boolean
+  operators found in this
+  table: .
+  
+
+  
+    range type
+    exclude
+  
+  Constraints on Ranges
+  
+   While UNIQUE is a natural constraint for scalar
+   values, it is usually unsuitable for range types. Instead, an
+   exclusion constraint is often more appropriate
+   (see CREATE TABLE
+   ... CONSTRAINT ... EXCLUDE). Exclusion constraints allow the
+   specification of constraints such as "non-overlapping" on a range
+   type. For example:
+
+ALTER TABLE reservation
+  ADD EXCLUDE USING gist (during WITH &&);
+
+   That constraint will prevent any overlapping values from existing
+   in the table at the same time:
+
+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 )).
+
+  
+  
+   Combine range types and exclusion constraints
+   with btree_gist for maximum
+   flexibility defining
+   constraints. After btree_gist is installed, the
+   following constraint will prevent overlapping ranges only if the
+   meeting room numbers are equal:
+
+
+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
+
+
+  
+
index ea45fadae697b5b7929c0c74f1992e82ab774e1d..ebcd461bd9149ca8c7c34c65d43f98ee2b6c5b52 100644 (file)
@@ -27,6 +27,15 @@ CREATE TYPE name AS
 CREATE TYPE name AS ENUM
     ( [ 'label' [, ... ] ] )
 
+CREATE TYPE name AS RANGE (
+    SUBTYPE = subtype,
+    [ , SUBTYPE_OPCLASS = subtype_operator_class ]
+    [ , SUBTYPE_DIFF = subtype_diff_function ]
+    [ , CANONICAL = canonical_function ]
+    [ , ANALYZE = analyze_function ]
+    [ , COLLATION = collation ]
+)
+
 CREATE TYPE name (
     INPUT = input_function,
     OUTPUT = output_function
@@ -98,11 +107,61 @@ CREATE TYPE name
    
   
 
+  
+   Range Types
+
+  
+    The third form of CREATE TYPE creates a new
+    range type, as described in .
+  
+
+  
+    The subtype parameter
+    can be any type with an associated btree opclass (uses the type's
+    default btree operator class unless specified with
+    subtype_operator_class).
+  
+
+  
+    The subtype_diff
+    function takes two values of type
+    subtype as argument, and
+    returns the distance between the two values as
+    double precision. This function is used for GiST indexing
+    (see  for more information), and should be provided
+    for efficiency.
+  
+
+  
+    The canonical
+    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 
+    for more information. To define
+    a canonical function,
+    you must first create a shell type, which is a
+    placeholder type that has no properties except a name and an
+    owner.  This is done by issuing the command CREATE TYPE
+   name, with no additional parameters.
+  
+
+  
+    The analyze
+    function is the same as for creating a base type.
+  
+
+  
+    The collation option
+    specifies the collation used when determining the total order for
+    the range.
+  
+  
+
   
    Base Types
 
   
-   The third form of CREATE TYPE creates a new base type
+   The fourth form of CREATE TYPE 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.)
index 34e2cc29150534aa1005702a3c34eb0b443eb550..70643122046f1787574054807a6bf384b74c16da 100644 (file)
@@ -997,8 +997,8 @@ $$ LANGUAGE SQL;
     
      SQL functions can be declared to accept and
      return the polymorphic types anyelement,
-     anyarrayanynonarray, and
-     anyenum.  See 
+     anyarrayanynonarray,
+     anyenum, and anyrange.  See 
      linkend="extend-types-polymorphic"> for a more detailed
      explanation of polymorphic functions. Here is a polymorphic
      function make_array 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
      anyelementanyarrayanynonarray,
-     and anyenum.
+     anyenum, and anyrange.
      See  for a more detailed explanation
      of polymorphic functions. When function arguments or return types
      are defined as polymorphic types, the function author cannot know
index 7e0b7d65a8749815475a0d625f508fbb5ccc2a3c..5a4419d3a80fc7754be99f36748a6862ed6196f0 100644 (file)
@@ -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 \
     )
 
index 5f7d7f6b68b25956f225cf501b69e20765a8537c..8378c360b97f540821840b05e04e3f54acf3e056 100644 (file)
@@ -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 (file)
index 0000000..3b90033
--- /dev/null
@@ -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);
+}
index 5069c5759ec60e730a70f98d2145dc78b200e156..91488bbbf5c9487381ea91a5aa8372dcb0fb03f1 100644 (file)
 #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
  *
index 398bc40c490a188a28a8657b01cb021b353e6c60..45ca5ec4c7aca0453f9f2dacd16ecd8aebdfc82c 100644 (file)
@@ -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)
    {
        /*
index 24ac5295f6077ca5eead5de510c8b8a1f0a55c92..63958c3afc6a6a0638ce62aa076254d0816fe8a7 100644 (file)
@@ -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;
index 4052a9a1fc98a58e209cba9fadb1555b3ecc6349..f3a34a12e20e88d906568d6eb36683a1c0adc713 100644 (file)
@@ -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;
index f0a4b0efa4a3137e596b0f6b0effa4a0e4347444..c135465fbd38cc67301ee137b083f8c5ea8baf14 100644 (file)
@@ -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);
index 127818abdeaf04a2a5449fd5cc56178e07d4cb93..a461ac9aef1c60e10a0081e33ca4814a951f35a0 100644 (file)
@@ -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))
index a74019a0d65a76445952833acc21d194378b346a..5b0633398cf631e3cd33bce1ae5432d76d04e8ee 100644 (file)
@@ -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;
index ce28abd9fe1e5d725c52a236c507f5fd731552d3..5f968b011fcd637631eaceca082fd75d90c9360d 100644 (file)
@@ -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 \
index b06faf0c720e0afd152eb705f930da3cd96b2c25..271b57a83027c7f11e5d2f771e183829ffd35a94 100644 (file)
@@ -889,7 +889,6 @@ date_timestamp(PG_FUNCTION_ARGS)
    PG_RETURN_TIMESTAMP(result);
 }
 
-
 /* timestamp_date()
  * Convert timestamp to date data type.
  */
index ddb1bd2b71ccf577b7cec15942bd257e5ad83e23..3b78c36a7ec6f0de98b9587ae0e2c374bf19d3ab 100644 (file)
@@ -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 (file)
index 0000000..6327d33
--- /dev/null
@@ -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.
+ *
+ *     := EMPTY
+ *            |   
+ *    := '[' | '('
+ *    := ']' | ')'
+ *
+ * Whitespace before or after  is ignored. Whitespace within a 
+ * is taken literally and becomes the input string for that bound.
+ *
+ * A  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 , 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 (file)
index 0000000..9dc7fd1
--- /dev/null
@@ -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);
+}
index 326f1eee92dfc84101dd64364b3fcbb1e623f74c..1b4d26d6593f843d6c7dd2c025ebe2fe3b19df77 100644 (file)
@@ -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;
+}
index 99e5f1d9fe616cbd10458d5457163205b2e97cb6..71b09abb232c93f26201fde1e949e1cfb46a2203 100644 (file)
@@ -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,
index 0911c8083be36954a7232c8ce4dc73e7190bfb61..3cc2a7ee0758112b43f7919da812eb48f53775ba 100644 (file)
@@ -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)
index c17b52cea8da028985e90b1e0ca8718f33caf993..88a867fe8e33a9bcbb830ff54287d299642cb8bc 100644 (file)
@@ -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
index c6273c12671d7e37874a301f7b29cf7d762ebe0f..0a0ebcf91666199bd89f21c09b3ae97570743d6e 100644 (file)
@@ -53,6 +53,7 @@
  */
 
 /*                         yyyymmddN */
-#define CATALOG_VERSION_NO 201110221
+/* COMMITTER: please set appropriately */
+#define CATALOG_VERSION_NO 201111111
 
 #endif
index 9a8e6ffc8a549ed405bea969f7e08f3984f0f24d..0bf2d4d640957957ebf5e5720601da189f36d21d 100644 (file)
@@ -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
 
index 3b88c41599e77c690e27c7f7344d6c8cebb29876..ede0c2ddf64d14783d04cd81a501661bc647955c 100644 (file)
@@ -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 */
index 9e2da2c30b2afe368408cec24588f909ccad42a2..e5d43f7c1d3ef02322ad2c5b376885a17e5f7430 100644 (file)
@@ -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 */
index d723b2561ae92796e8d67d338a37fdedc99793c1..05ffa0384cff89d6e6475a2175f8504d4b4869fa 100644 (file)
@@ -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 */
index 64f1391b00059d82228c779ae31e3fd1a197c984..f587f5b198df3741a3ea4e2a18d907a947a90fff 100644 (file)
@@ -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
index 548727dbd2474236cc6b0d6ed483eaafd18c130f..5ea949bec6b006ffe67d38539bb06abf6a807c41 100644 (file)
@@ -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 */
index 64b7a6a314d1084665caa5923fda8629ebd2f81b..3b654ff7c45ad2d7defd069364bb7a9d500e9849 100644 (file)
@@ -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 (file)
index 0000000..19b437d
--- /dev/null
@@ -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 */
index d72ca2342fabb33239ceed03cb85ecaeacd0fe7c..b24fbc97f4e71182a6c9d59bc41244d2cf0b310c 100644 (file)
@@ -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 */
index 429a964f913fa2dc145bd384a036d10419d49cca..0c328958635ed250a44c610b802e05fd4c12ef63 100644 (file)
@@ -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);
index 7aa299485fcaff78c64afd626c2e917ccb6a4773..824d8b5dc9a24146fe520806a37b20edff02d262 100644 (file)
@@ -346,6 +346,7 @@ typedef enum NodeTag
    T_ReassignOwnedStmt,
    T_CompositeTypeStmt,
    T_CreateEnumStmt,
+   T_CreateRangeStmt,
    T_AlterEnumStmt,
    T_AlterTSDictionaryStmt,
    T_AlterTSConfigurationStmt,
index 9998e2f24d6793ca17b6fd0ec93f48d4643898aa..af6565e7e4a805857ff18d6da2e46e210aad4c09 100644 (file)
@@ -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
  * ----------------------
index 215951589af3d19c40262bf2892ed1cfeef12c23..d3ad4f14282641fc7ba35f3188d76a8b097dd22e 100644 (file)
@@ -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 (file)
index 0000000..a7595b1
--- /dev/null
@@ -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 */
index 55d22303a7ac1c166a15dfea8d4554554b5d8cb8..35782fc3e7b28c31e3702ca4a43e5271a139e091 100644 (file)
@@ -70,6 +70,7 @@ enum SysCacheIdentifier
    OPFAMILYOID,
    PROCNAMEARGSNSP,
    PROCOID,
+   RANGETYPE,
    RELNAMENSP,
    RELOID,
    RULERELNAME,
index 578cae57346f46cf11404499fd89630db239e1fe..48399d3929ceb1b36b6ebb693be16695814bd2d0 100644 (file)
@@ -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;
index 8cee6ed81275736c0f7e9e5313596b8d3aa60277..f9659f7739e256fdaf1288394012984878791440 100644 (file)
@@ -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;
index a25f90cbfd28ff82c446fc326a8da79a66cd00a5..19b559ffa17d1bc52230bc277019b4e2260342ac 100644 (file)
@@ -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).
index 238bf5f0aec5e85e6af8598e4174797116644e25..fc9d4019444a733d768827afa9c699c404636c57 100644 (file)
@@ -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 (file)
index 0000000..495508c
--- /dev/null
@@ -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.
index d42b0ea045bf89b7b8a4c56eded03d7a4848682b..38c88d977ad81e8170352c0b5669449ad5906ebe 100644 (file)
@@ -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
index e30ecbc6feba07d49bec5c27204b45418c096ccf..7ca7a95ec66c32d782d7ddb7f4a9eabdd7a31b7e 100644 (file)
@@ -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'));
index 376f28d99a1ead3a0c2c43cfe07d4067e9f24c9f..70976d115cde16d8a3db55b00fdf0041869f5a3c 100644 (file)
@@ -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
index bb654f9c612970ef777e8cc39369a91e343f6afc..2e87d9eefd6fbb70a5603bc000ffe8335945201f 100644 (file)
@@ -18,6 +18,7 @@ test: txid
 test: uuid
 test: enum
 test: money
+test: rangetypes
 test: strings
 test: numerology
 test: point
index 73f5587b700f5c616c95007d73652e987386e242..3b7cc6cf2bcc086016cb3ae2b04ad8e8915e9bb2 100644 (file)
@@ -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;
index 65ae868d989388e1cc28281ed4b49419764fe251..7f936c815474205dc68cec8d5db96f786aadae9f 100644 (file)
@@ -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]
index b47c2de312a102a1342f54c5101be95edfa4d3b2..2906943f06fbac2a777960b3cbeb0b5303d40938 100644 (file)
@@ -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 (file)
index 0000000..44885c8
--- /dev/null
@@ -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;
index fa6dd75f07fd4066f12fad76fb651c07b9c35e9d..1638861bc1dd8e270739a7c1191dfa11d2059d9e 100644 (file)
@@ -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'));