*** a/doc/src/sgml/datatype.sgml
--- b/doc/src/sgml/datatype.sgml
***************
*** 4190,4195 **** SET xmloption TO { DOCUMENT | CONTENT };
--- 4190,4197 ----
&rowtypes;
+ &rangetypes;
+
Object Identifier Types
***************
*** 4461,4466 **** SELECT * FROM pg_attribute
--- 4463,4472 ----
+ anyrange
+
+
+ void
***************
*** 4537,4542 **** SELECT * FROM pg_attribute
--- 4543,4555 ----
+ anyrange>
+ Indicates that a function accepts any range data type
+ (see and
+ ).
+
+
+ anynonarray>Indicates that a function accepts any non-array data type
(see ).
***************
*** 4600,4606 **** 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>.
--- 4613,4620 ----
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>, anyrange>, and
! anynonarray>.
*** a/doc/src/sgml/extend.sgml
--- b/doc/src/sgml/extend.sgml
***************
*** 198,211 ****
! 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.
--- 198,212 ----
! 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.
***************
*** 221,226 ****
--- 222,232 ----
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.
*** a/doc/src/sgml/filelist.sgml
--- b/doc/src/sgml/filelist.sgml
***************
*** 25,30 ****
--- 25,31 ----
+
*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
***************
*** 10444,10449 **** SELECT NULLIF(value, '(none)') ...
--- 10444,10906 ----
+
+ Range Functions and Operators
+
+
+ shows the operators
+ available for range types.
+
+
+
+ Range Operators
+
+
+
+ Operator
+ Description
+ Example
+ Result
+
+
+
+
+ =
+ equal
+ range(1,5) = '[1,4]'::int4range
+ t
+
+
+
+ <>
+ not equal
+ range(1.1,2.2) <> range(1.1,2.3)
+ t
+
+
+
+ <
+ less than
+ range(1,10) < range(2,3)
+ t
+
+
+
+ >
+ greater than
+ range(1,10) > range(1,5)
+ t
+
+
+
+ <=
+ less than or equal
+ range(1.1,2.2) <= range(1.1,2.2)
+ t
+
+
+
+ >=
+ greater than or equal
+ range(1.1,2.2) >= range(1.1,2.0)
+ t
+
+
+
+ @>
+ contains
+ '[2011-01-01,2011-03-01)'::tsrange @> '2011-01-10'::timestamp
+ t
+
+
+
+ <@
+ is contained by
+ range(2,4) <@ range(1,7)
+ t
+
+
+
+ &&
+ overlap (have points in common)
+ range(3,7) && range(4,12)
+ t
+
+
+
+ <<
+ strictly left of
+ range(1,10) << range(100,110)
+ t
+
+
+
+ >>
+ strictly right of
+ range(50,60) >> range(20,30)
+ t
+
+
+
+ &<
+ Does not extend to the right of?
+ range(1,20) &< range(18,20)
+ t
+
+
+
+ &>
+ Does not extend to the left of?
+ range(7,20) &> range(5,10)
+ t
+
+
+
+ -|-
+ adjacent?
+ range(1.1,2.2) -|- range(2.2,3.3)
+ t
+
+
+
+ +
+ Union
+ range(5,15) + range(10,20)
+ [5,20)
+
+
+
+ -
+ Difference
+ range(5,15) - range(10,20)
+ [5,10)
+
+
+
+ *
+ Intersection
+ range(5,15) * range(10,20)
+ [10,15)
+
+
+
+ !?
+ Is empty?
+ '-'::int4range !?
+ t
+
+
+
+ ?
+ Is non-empty?
+ range(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.
+
+
+
+ Array comparisons compare the array contents element-by-element,
+ using the default B-tree comparison function for the element data type.
+ In multidimensional arrays the elements are visited in row-major order
+ (last subscript varies most rapidly).
+ If the contents of two arrays are equal but the dimensionality is
+ different, the first difference in the dimensionality information
+ determines the sort order. (This is a change from versions of
+ PostgreSQL> prior to 8.2: older versions would claim
+ that two arrays with the same contents were equal, even if the
+ number of dimensions or subscript ranges were different.)
+
+
+
+ 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.
+
+
+
+ range
+
+
+ range__
+
+
+ range_i
+
+
+ rangei_
+
+
+ rangeii
+
+
+ range_linf_
+
+
+ range_uinf_
+
+
+ range_linfi
+
+
+ range_uinfi
+
+
+ lower
+
+
+ upper
+
+
+ empty
+
+
+ non_empty
+
+
+ lower_inc
+
+
+ upper_inc
+
+
+ lower_inf
+
+
+ upper_inf
+
+
+ length
+
+
+
+ Range Functions
+
+
+
+ Function
+ Return Type
+ Description
+ Example
+ Result
+
+
+
+
+
+
+ range(anyelement, anyelement)
+
+
+ anyrange
+ alias for rangei_
+ range(1.1,2.2)
+ [1.1,2.2)
+
+
+
+
+ range__(anyelement, anyelement)
+
+
+ anyrange
+ construct a range with exclusive lower bound and exclusive upper bound
+ range__(1.1,2.2)
+ (1.1,2.2)
+
+
+
+
+ range_i(anyelement, anyelement)
+
+
+ anyrange
+ construct a range with exclusive lower bound and inclusive upper bound
+ range_i(1.1,2.2)
+ (1.1,2.2]
+
+
+
+
+ rangei_(anyelement, anyelement)
+
+
+ anyrange
+ construct a range with inclusive lower bound and exclusive upper bound
+ rangei_(1.1,2.2)
+ [1.1,2.2)
+
+
+
+
+ rangeii(anyelement, anyelement)
+
+
+ anyrange
+ construct a range with inclusive lower bound and inclusive upper bound
+ rangeii(1.1,2.2)
+ [1.1,2.2]
+
+
+
+
+ range_linf_(anyelement)
+
+
+ anyrange
+ construct a range with infinite lower bound and exclusive upper bound
+ range_linf_(1.1)
+ (-INF,1.1)
+
+
+
+
+ range_uinf_(anyelement)
+
+
+ anyrange
+ construct a range with exclusive lower bound and infinite upper bound
+ range_uinf_(1.1)
+ (1.1,INF)
+
+
+
+
+ range_linfi(anyelement)
+
+
+ anyrange
+ construct a range with infinite lower bound and inclusive upper bound
+ range_linfi(1.1)
+ (-INF,1.1]
+
+
+
+
+ range_uinfi(anyelement)
+
+
+ anyrange
+ construct a range with inclusive lower bound and infinite upper bound
+ range_uinfi(1.1)
+ [1.1,INF)
+
+
+
+
+ lower(anyrange)
+
+
+ anyrange
+ lower bound of range
+ lower(range(1.1,2.2))
+ 1.1
+
+
+
+
+ upper(anyrange)
+
+
+ anyrange
+ upper bound of range
+ upper(range(1.1,2.2))
+ 2.2
+
+
+
+
+ empty(anyrange)
+
+
+ anyrange
+ is the range empty?
+ empty(range(1.1,2.2))
+ false
+
+
+
+
+ non_empty(anyrange)
+
+
+ anyrange
+ is the range non-empty?
+ non_empty(range(1.1,2.2))
+ true
+
+
+
+
+ lower_inc(anyrange)
+
+
+ anyrange
+ is the lower bound of the range inclusive?
+ lower_inc(range(1.1,2.2))
+ true
+
+
+
+
+ upper_inc(anyrange)
+
+
+ anyrange
+ is the upper bound of the range inclusive?
+ upper_inc(range(1.1,2.2))
+ false
+
+
+
+
+ lower_inf(anyrange)
+
+
+ anyrange
+ is the lower bound of the range infinite?
+ lower_inf('(-INF,INF)'::daterange)
+ true
+
+
+
+
+ upper_inf(anyrange)
+
+
+ anyrange
+ is the upper bound of the range infinite?
+ upper_inf('(-INF,INF)'::daterange)
+ true
+
+
+
+
+ length(anyrange)
+
+
+ anyelement
+ what is the upper bound of the range minus the lower bound?
+ length(range(10.1,20.1))
+ 10.0
+
+
+
+
+
+
Aggregate Functions
*** a/doc/src/sgml/plpgsql.sgml
--- b/doc/src/sgml/plpgsql.sgml
***************
*** 139,145 ****
PL/pgSQL> functions can also be declared to accept
and return the polymorphic types
anyelement, anyarray, anynonarray,
! and anyenum>. The actual
data types handled by a polymorphic function can vary from call to
call, as discussed in .
An example is shown in .
--- 139,145 ----
PL/pgSQL> functions can also be declared to accept
and return the polymorphic types
anyelement, anyarray, anynonarray,
! 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,507 **** $$ LANGUAGE plpgsql;
When the return type of a PL/pgSQL
function is declared as a polymorphic type (anyelement,
! anyarray, anynonarray, or anyenum>),
! 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 ).
--- 500,507 ----
When the return type of a PL/pgSQL
function is declared as a polymorphic type (anyelement,
! anyarray, anynonarray, anyenum,
! 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 ).
*** /dev/null
--- b/doc/src/sgml/rangetypes.sgml
***************
*** 0 ****
--- 1,328 ----
+
+
+
+ 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
+
+ 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 .
+
+
+
+
+
+
+
+ Examples
+
+
+ CREATE TABLE reservation ( during TSRANGE );
+ INSERT INTO reservation VALUES
+ ( '[2010-01-01 14:30, 2010-01-01 15:30)' );
+
+ -- Containment
+ SELECT range(10, 20) @> 3;
+
+ -- Overlaps
+ SELECT range(11.1, 22.2) && range(20.0, 30.0)
+
+ -- Find the upper bound:
+ SELECT upper(range(15, 25));
+
+ -- Compute the intersection:
+ SELECT range(10, 20) * range(15, 25);
+
+ -- Is the range non-empty?
+ SELECT range(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 "(". 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 -INF, meaning
+ that all points less than the upper bound are included in the
+ range. Likewise, if the upper bound of the range
+ is INF, then all points greater than the lower
+ bound are included in the range. If both lower and upper bounds are
+ infinite, 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 ]
+ -
+
+ Notice that the final pattern is a "-", 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 -INF;
+ and upper-bound may be either a string
+ that is valid input for the sub-type, or INF.
+
+
+ Either the lower-bound or
+ the upper-bound may be quoted
+ using "..." (double quotation), which will
+ preserve internal whitespace and 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 .
+
+
+
+
+ Constructing Ranges
+
+ Some examples of constructing ranges from existing values:
+
+ SELECT range_i(1.0, 14.0);
+
+ -- the int4range result will appear in the canonical form; see
+ SELECT range_i(1, 14);
+
+ SELECT range_uinfi(11.1);
+
+ SELECT range_linf_(2.2);
+
+
+
+ The function names follow a pattern where the suffix
+ "_" is used to mean "exclusive" and
+ "i" is used to mean "inclusive". In the case of a
+ two-character suffix, the first character refers to the lower bound,
+ and the second refers to the upper bound. The
+ function range assumes an inclusive lower bound
+ and an exclusive upper bound; and is an alias
+ for rangei_.
+
+
+ For a complete set of constructors, see .
+
+
+
+
+ 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.
+
+
+ For types such as NUMRANGE, this is not possible,
+ because there are always points in between two
+ distinct NUMERIC values.
+
+
+
+
+ Defining New Range Types
+
+ To define a new range type of sub-type DOUBLE PRECISION:
+
+ CREATE TYPE FLOATRANGE AS RANGE (
+ SUBTYPE = DOUBLE PRECISION,
+ SUBTYPE_CMP = btfloat8cmp
+ );
+
+ SELECT '[1.234, 5.678]'::floatrange;
+
+ Because DOUBLE PRECISION has no meaningful "step", we
+ do not define a canonical
+ function. See for more
+ information.
+
+
+
+
+
+ range type
+ gist
+
+ 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 many other
+ 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
+
+
+
+
+
*** a/doc/src/sgml/ref/create_type.sgml
--- b/doc/src/sgml/ref/create_type.sgml
***************
*** 27,32 **** CREATE TYPE name AS
--- 27,40 ----
CREATE TYPE name AS ENUM
( [ 'label' [, ... ] ] )
+ CREATE TYPE name AS RANGE (
+ SUBTYPE = subtype,
+ SUBTYPE_CMP = subtype_cmp_function
+ [ , SUBTYPE_FLOAT = subtype_float_function ]
+ [ , CANONICAL = canonical_function ]
+ [ , ANALYZE = analyze_function ]
+ )
+
CREATE TYPE name (
INPUT = input_function,
OUTPUT = output_function
***************
*** 98,108 **** CREATE TYPE nameBase Types
! The third 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.)
--- 106,165 ----
+
+ Range Types
+
+
+ The third form of CREATE TYPE creates a new
+ range type, as described in . To create
+ a new range type, you must be a superuser.
+
+
+
+ The subtype parameter
+ can be any type. The
+ function subtype_cmp
+ must take two arguments of
+ type subtype, and
+ compare them in a total order,
+ returning -1, 0,
+ or 1 when the first argument is less than,
+ equal to, or greater than the second argument, respectively.
+
+
+
+ The subtype_float
+ function takes a value of
+ type subtype and
+ returns a value of type 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.
+
+
+
Base Types
! 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.)
*** a/doc/src/sgml/xfunc.sgml
--- b/doc/src/sgml/xfunc.sgml
***************
*** 997,1004 **** $$ LANGUAGE SQL;
SQL functions can be declared to accept and
return the polymorphic types anyelement,
! anyarray, anynonarray, and
! anyenum. See for a more detailed
explanation of polymorphic functions. Here is a polymorphic
function make_array that builds up an array
--- 997,1004 ----
SQL functions can be declared to accept and
return the polymorphic types anyelement,
! anyarray, anynonarray,
! anyenum, and anyrange. See for a more detailed
explanation of polymorphic functions. Here is a polymorphic
function make_array that builds up an array
***************
*** 3046,3052 **** CREATE OR REPLACE FUNCTION retcomposite(IN integer, IN integer,
C-language functions can be declared to accept and
return the polymorphic types
anyelement, anyarray, anynonarray,
! and anyenum.
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
--- 3046,3052 ----
C-language functions can be declared to accept and
return the polymorphic types
anyelement, anyarray, anynonarray,
! 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
*** a/src/backend/catalog/Makefile
--- b/src/backend/catalog/Makefile
***************
*** 13,20 **** 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
BKIFILES = postgres.bki postgres.description postgres.shdescription
--- 13,20 ----
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_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,45 **** 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 \
toasting.h indexing.h \
)
--- 39,45 ----
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_range.h \
toasting.h indexing.h \
)
*** a/src/backend/catalog/pg_proc.c
--- b/src/backend/catalog/pg_proc.c
***************
*** 166,171 **** ProcedureCreate(const char *procedureName,
--- 166,172 ----
case ANYELEMENTOID:
case ANYNONARRAYOID:
case ANYENUMOID:
+ case ANYRANGEOID:
genericInParam = true;
break;
case INTERNALOID:
***************
*** 190,195 **** ProcedureCreate(const char *procedureName,
--- 191,197 ----
case ANYELEMENTOID:
case ANYNONARRAYOID:
case ANYENUMOID:
+ case ANYRANGEOID:
genericOutParam = true;
break;
case INTERNALOID:
*** /dev/null
--- b/src/backend/catalog/pg_range.c
***************
*** 0 ****
--- 1,135 ----
+ /*-------------------------------------------------------------------------
+ *
+ * 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/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,
+ regproc rangeSubOpclass, regproc rangeCanonical,
+ regproc rangeSubFloat)
+ {
+ 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_rngsubfloat - 1] = ObjectIdGetDatum(rangeSubFloat);
+
+ 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(rangeSubFloat))
+ {
+ referenced.classId = ProcedureRelationId;
+ referenced.objectId = rangeSubFloat;
+ 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);
+ }
*** a/src/backend/commands/typecmds.c
--- b/src/backend/commands/typecmds.c
***************
*** 43,48 ****
--- 43,50 ----
#include "catalog/pg_depend.h"
#include "catalog/pg_enum.h"
#include "catalog/pg_namespace.h"
+ #include "catalog/pg_proc.h"
+ #include "catalog/pg_range.h"
#include "catalog/pg_type.h"
#include "catalog/pg_type_fn.h"
#include "commands/defrem.h"
***************
*** 87,92 **** static Oid findTypeSendFunction(List *procname, Oid typeOid);
--- 89,97 ----
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 findRangeSubtypeFloatFunction(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,100 **** static char *domainAddConstraint(Oid domainOid, Oid domainNamespace,
--- 100,106 ----
Oid baseTypeOid,
int typMod, Constraint *constr,
char *domainName);
+ static HeapTuple OpClassCacheLookup(Oid amID, List *opclassname, bool missing_ok);
/*
***************
*** 735,740 **** RemoveTypeById(Oid typeOid)
--- 741,754 ----
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);
***************
*** 1227,1232 **** DefineEnum(CreateEnumStmt *stmt)
--- 1241,1513 ----
}
/*
+ * 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 *rangeSubtypeFloatName = NIL;
+ List *rangeCollationName = NIL;
+ Oid rangeCollation = InvalidOid;
+ regproc rangeAnalyze = InvalidOid;
+ Oid rangeSubtype = InvalidOid;
+ regproc rangeSubOpclass = InvalidOid;
+ regproc rangeCanonical = InvalidOid;
+ regproc rangeSubtypeFloat = InvalidOid;
+
+
+ /*
+ * As of Postgres 8.4, we require superuser privilege to create a base
+ * type. This is simple paranoia: there are too many ways to mess up the
+ * system with an incorrect type definition (for instance, representation
+ * parameters that don't match what the C code expects). In practice it
+ * takes superuser privilege to create the I/O functions, and so the
+ * former requirement that you own the I/O functions pretty much forced
+ * superuserness anyway. We're just making doubly sure here.
+ *
+ * XXX re-enable NOT_USED code sections below if you remove this test.
+ */
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be superuser to create a range type")));
+
+ /* Convert list of names to a name and namespace */
+ typeNamespace = QualifiedNameGetCreationNamespace(stmt->typeName,
+ &typeName);
+
+ #ifdef NOT_USED
+ /* XXX this is unnecessary given the superuser check above */
+ /* 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));
+ #endif
+
+ /*
+ * 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)));
+ }
+
+ /* TODO: parse parameters */
+ 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_float") == 0)
+ {
+ if (rangeSubtypeFloatName != NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options")));
+ rangeSubtypeFloatName = 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 (rangeSubtypeFloatName != NIL)
+ rangeSubtypeFloat = findRangeSubtypeFloatFunction(
+ rangeSubtypeFloatName, 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, rangeSubtypeFloat);
+
+ /*
+ * 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);
+ }
+
+ /*
* AlterEnum
* Adds a new label to an existing enum.
*/
***************
*** 1542,1547 **** findTypeAnalyzeFunction(List *procname, Oid typeOid)
--- 1823,1943 ----
}
/*
+ * 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;
+ HeapTuple opctuple;
+
+ if (opcname == NIL)
+ {
+ opcid = GetDefaultOpClass(subtype, BTREE_AM_OID);
+ if (!OidIsValid(opcid))
+ {
+ HeapTuple amtuple;
+ Form_pg_am pg_am;
+ amtuple = SearchSysCache1(AMOID,
+ ObjectIdGetDatum(BTREE_AM_OID));
+ if (!HeapTupleIsValid(amtuple))
+ elog(ERROR, "cache lookup failed for access method %u",
+ BTREE_AM_OID);
+ pg_am = (Form_pg_am) GETSTRUCT(amtuple);
+
+ /*
+ * No need to call ReleaseSysCache because this is only an
+ * error path.
+ */
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("data type %s has no default operator class for access method \"%s\"",
+ format_type_be(subtype), NameStr(pg_am->amname)),
+ errhint("You must specify an operator class for the data type or define a default operator class for the data type.")));
+ }
+ return opcid;
+ }
+
+
+ opctuple = OpClassCacheLookup(BTREE_AM_OID, opcname, false);
+
+ opcid = HeapTupleGetOid(opctuple);
+
+ ReleaseSysCache(opctuple);
+
+ return opcid;
+ }
+
+ /*
+ * Used to find a range's 'canonical' function.
+ */
+ static Oid
+ findRangeSubtypeFloatFunction(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) != FLOAT8OID)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("range subtype float function %s must return type \"float8\"",
+ func_signature_string(procname, 1, NIL, argList))));
+
+ if (func_volatile(procOid) != PROVOLATILE_IMMUTABLE)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("range subtype float function %s must be immutable",
+ func_signature_string(procname, 1, 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
*
* Pre-assign the type's array OID for use in pg_type.typarray
***************
*** 3072,3074 **** AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
--- 3468,3526 ----
return oldNspOid;
}
+
+
+ /*
+ * OpClassCacheLookup
+ * Look up an existing opclass by name.
+ *
+ * Returns a syscache tuple reference, or NULL if not found.
+ */
+ static HeapTuple
+ OpClassCacheLookup(Oid amID, List *opclassname, bool missing_ok)
+ {
+ char *schemaname;
+ char *opcname;
+ HeapTuple htup;
+
+ /* deconstruct the name list */
+ DeconstructQualifiedName(opclassname, &schemaname, &opcname);
+
+ if (schemaname)
+ {
+ /* Look in specific schema only */
+ Oid namespaceId;
+
+ namespaceId = LookupExplicitNamespace(schemaname);
+ htup = SearchSysCache3(CLAAMNAMENSP,
+ ObjectIdGetDatum(amID),
+ PointerGetDatum(opcname),
+ ObjectIdGetDatum(namespaceId));
+ }
+ else
+ {
+ /* Unqualified opclass name, so search the search path */
+ Oid opcID = OpclassnameGetOpcid(amID, opcname);
+
+ if (!OidIsValid(opcID))
+ htup = NULL;
+ else
+ htup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opcID));
+ }
+
+ if (!HeapTupleIsValid(htup) && !missing_ok)
+ {
+ HeapTuple amtup;
+
+ amtup = SearchSysCache1(AMOID, ObjectIdGetDatum(amID));
+ if (!HeapTupleIsValid(amtup))
+ elog(ERROR, "cache lookup failed for access method %u", amID);
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("operator class \"%s\" does not exist for access method \"%s\"",
+ NameListToString(opclassname),
+ NameStr(((Form_pg_am) GETSTRUCT(amtup))->amname))));
+ }
+
+ return htup;
+ }
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
***************
*** 3034,3039 **** _copyCreateEnumStmt(CreateEnumStmt *from)
--- 3034,3050 ----
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)
{
***************
*** 4273,4278 **** copyObject(void *from)
--- 4284,4292 ----
case T_CreateEnumStmt:
retval = _copyCreateEnumStmt(from);
break;
+ case T_CreateRangeStmt:
+ retval = _copyCreateRangeStmt(from);
+ break;
case T_AlterEnumStmt:
retval = _copyAlterEnumStmt(from);
break;
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
***************
*** 1439,1444 **** _equalCreateEnumStmt(CreateEnumStmt *a, CreateEnumStmt *b)
--- 1439,1453 ----
}
static bool
+ _equalCreateRangeStmt(CreateRangeStmt *a, CreateRangeStmt *b)
+ {
+ COMPARE_NODE_FIELD(typeName);
+ COMPARE_NODE_FIELD(params);
+
+ return true;
+ }
+
+ static bool
_equalAlterEnumStmt(AlterEnumStmt *a, AlterEnumStmt *b)
{
COMPARE_NODE_FIELD(typeName);
***************
*** 2826,2831 **** equal(void *a, void *b)
--- 2835,2843 ----
case T_CreateEnumStmt:
retval = _equalCreateEnumStmt(a, b);
break;
+ case T_CreateRangeStmt:
+ retval = _equalCreateRangeStmt(a, b);
+ break;
case T_AlterEnumStmt:
retval = _equalAlterEnumStmt(a, b);
break;
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 4327,4332 **** DefineStmt:
--- 4327,4339 ----
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);
*** a/src/backend/parser/parse_coerce.c
--- b/src/backend/parser/parse_coerce.c
***************
*** 160,166 **** coerce_type(ParseState *pstate, Node *node,
return node;
}
if (targetTypeId == ANYARRAYOID ||
! targetTypeId == ANYENUMOID)
{
/*
* Assume can_coerce_type verified that implicit coercion is okay.
--- 160,167 ----
return node;
}
if (targetTypeId == ANYARRAYOID ||
! targetTypeId == ANYENUMOID ||
! targetTypeId == ANYRANGEOID)
{
/*
* Assume can_coerce_type verified that implicit coercion is okay.
***************
*** 1313,1318 **** check_generic_type_consistency(Oid *actual_arg_types,
--- 1314,1321 ----
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;
***************
*** 1350,1355 **** check_generic_type_consistency(Oid *actual_arg_types,
--- 1353,1367 ----
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 */
***************
*** 1395,1400 **** check_generic_type_consistency(Oid *actual_arg_types,
--- 1407,1433 ----
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;
}
***************
*** 1475,1481 **** enforce_generic_type_consistency(Oid *actual_arg_types,
--- 1508,1516 ----
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);
***************
*** 1536,1541 **** enforce_generic_type_consistency(Oid *actual_arg_types,
--- 1571,1596 ----
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;
+ }
}
/*
***************
*** 1581,1586 **** enforce_generic_type_consistency(Oid *actual_arg_types,
--- 1636,1669 ----
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)
***************
*** 1647,1652 **** enforce_generic_type_consistency(Oid *actual_arg_types,
--- 1730,1748 ----
}
declared_arg_types[j] = array_typeid;
}
+ else if (decl_type == ANYRANGEOID)
+ {
+ if (!OidIsValid(range_typeid))
+ {
+ range_typeid = get_range_from_subtype(elem_typeid);
+ 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;
+ }
}
}
***************
*** 1665,1670 **** enforce_generic_type_consistency(Oid *actual_arg_types,
--- 1761,1781 ----
return array_typeid;
}
+ /* if we return ANYRANGE use the appropriate argument type */
+ if (rettype == ANYRANGEOID)
+ {
+ if (!OidIsValid(range_typeid))
+ {
+ range_typeid = get_range_from_subtype(elem_typeid);
+ 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 ||
***************
*** 1713,1719 **** resolve_generic_type(Oid declared_type,
}
else if (context_declared_type == ANYELEMENTOID ||
context_declared_type == ANYNONARRAYOID ||
! context_declared_type == ANYENUMOID)
{
/* Use the array type corresponding to actual type */
Oid array_typeid = get_array_type(context_actual_type);
--- 1824,1831 ----
}
else if (context_declared_type == ANYELEMENTOID ||
context_declared_type == ANYNONARRAYOID ||
! 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);
***************
*** 1728,1734 **** resolve_generic_type(Oid declared_type,
}
else if (declared_type == ANYELEMENTOID ||
declared_type == ANYNONARRAYOID ||
! declared_type == ANYENUMOID)
{
if (context_declared_type == ANYARRAYOID)
{
--- 1840,1847 ----
}
else if (declared_type == ANYELEMENTOID ||
declared_type == ANYNONARRAYOID ||
! declared_type == ANYENUMOID ||
! declared_type == ANYRANGEOID)
{
if (context_declared_type == ANYARRAYOID)
{
***************
*** 1743,1748 **** resolve_generic_type(Oid declared_type,
--- 1856,1873 ----
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 an 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)
***************
*** 1856,1861 **** IsBinaryCoercible(Oid srctype, Oid targettype)
--- 1981,1991 ----
if (type_is_enum(srctype))
return true;
+ /* Also accept any enum 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))
*** a/src/backend/tcop/utility.c
--- b/src/backend/tcop/utility.c
***************
*** 202,207 **** check_xact_readonly(Node *parsetree)
--- 202,208 ----
case T_CreateTrigStmt:
case T_CompositeTypeStmt:
case T_CreateEnumStmt:
+ case T_CreateRangeStmt:
case T_AlterEnumStmt:
case T_ViewStmt:
case T_DropCastStmt:
***************
*** 913,918 **** standard_ProcessUtility(Node *parsetree,
--- 914,923 ----
DefineEnum((CreateEnumStmt *) parsetree);
break;
+ case T_CreateRangeStmt:
+ DefineRange((CreateRangeStmt *) parsetree);
+ break;
+
case T_AlterEnumStmt: /* ALTER TYPE (enum) */
/*
***************
*** 1897,1902 **** CreateCommandTag(Node *parsetree)
--- 1902,1911 ----
tag = "CREATE TYPE";
break;
+ case T_CreateRangeStmt:
+ tag = "CREATE TYPE";
+ break;
+
case T_AlterEnumStmt:
tag = "ALTER TYPE";
break;
***************
*** 2444,2449 **** GetCommandLogLevel(Node *parsetree)
--- 2453,2462 ----
lev = LOGSTMT_DDL;
break;
+ case T_CreateRangeStmt:
+ lev = LOGSTMT_DDL;
+ break;
+
case T_AlterEnumStmt:
lev = LOGSTMT_DDL;
break;
*** a/src/backend/utils/adt/Makefile
--- b/src/backend/utils/adt/Makefile
***************
*** 20,27 **** 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 \
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 \
--- 20,27 ----
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 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 \
*** a/src/backend/utils/adt/date.c
--- b/src/backend/utils/adt/date.c
***************
*** 888,893 **** date_timestamp(PG_FUNCTION_ARGS)
--- 888,900 ----
PG_RETURN_TIMESTAMP(result);
}
+ Datum
+ date_float8(PG_FUNCTION_ARGS)
+ {
+ DateADT dateVal = PG_GETARG_DATEADT(0);
+
+ PG_RETURN_FLOAT8((float8) dateVal);
+ }
/* timestamp_date()
* Convert timestamp to date data type.
*** a/src/backend/utils/adt/pseudotypes.c
--- b/src/backend/utils/adt/pseudotypes.c
***************
*** 25,30 ****
--- 25,31 ----
#include "libpq/pqformat.h"
#include "utils/array.h"
#include "utils/builtins.h"
+ #include "utils/rangetypes.h"
/*
***************
*** 187,192 **** anyenum_out(PG_FUNCTION_ARGS)
--- 188,216 ----
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.
*** /dev/null
--- b/src/backend/utils/adt/rangetypes.c
***************
*** 0 ****
--- 1,1864 ----
+ /*-------------------------------------------------------------------------
+ *
+ * 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 "utils/builtins.h"
+ #include "utils/date.h"
+ #include "utils/fmgroids.h"
+ #include "utils/lsyscache.h"
+ #include "utils/rangetypes.h"
+ #include "utils/syscache.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
+ #define RANGE_LB_INF 0x08
+ #define RANGE_UB_INC 0x10
+ #define RANGE_UB_NULL 0x20
+ #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)))
+ typedef enum
+ {
+ RANGE_PSTATE_INIT,
+ RANGE_PSTATE_PRE_LB,
+ RANGE_PSTATE_LB,
+ RANGE_PSTATE_SEP,
+ RANGE_PSTATE_PRE_UB,
+ RANGE_PSTATE_UB,
+ RANGE_PSTATE_UB_INC,
+ RANGE_PSTATE_DONE
+ } RangePState;
+
+
+ static void range_parse(const char *input_str, char *flags,
+ char **lbound_str, char **ubound_str);
+ static char *range_deparse(char flags, char *lbound_str, char *ubound_str);
+ static Datum range_make2(PG_FUNCTION_ARGS);
+ static bool range_contains_internal(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);
+ static void range_subtype_cmpfn(Oid rngtypid, FmgrInfo *flinfo,
+ Oid *collation);
+
+ /*
+ *----------------------------------------------------------
+ * 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;
+
+ Oid subtype;
+ regproc subInput;
+ FmgrInfo subInputFn;
+ Oid ioParam;
+
+ RangeBound lower;
+ RangeBound upper;
+
+ if (rngtypoid == ANYRANGEOID)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot accept a value of type anyrange")));
+
+ subtype = get_range_subtype(rngtypoid);
+
+ /* parse */
+ range_parse(input_str, &flags, &lbound_str, &ubound_str);
+
+ /* input */
+ getTypeInputInfo(subtype, &subInput, &ioParam);
+ fmgr_info(subInput, &subInputFn);
+
+ lower.rngtypid = rngtypoid;
+ lower.infinite = flags & RANGE_LB_INF;
+ lower.inclusive = flags & RANGE_LB_INC;
+ lower.lower = true;
+ upper.rngtypid = rngtypoid;
+ upper.infinite = flags & RANGE_UB_INF;
+ upper.inclusive = flags & RANGE_UB_INC;
+ 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(&lower, &upper, flags & RANGE_EMPTY);
+
+ PG_RETURN_RANGE(range);
+ }
+
+ Datum
+ range_out(PG_FUNCTION_ARGS)
+ {
+ RangeType *range = PG_GETARG_RANGE(0);
+
+ Oid subtype;
+
+ regproc subOutput;
+ FmgrInfo subOutputFn;
+ bool isVarlena;
+
+ char flags = 0;
+ char *lbound_str = NULL;
+ char *ubound_str = NULL;
+ char *output_str;
+
+ bool empty;
+ RangeBound lower;
+ RangeBound upper;
+
+ /* deserialize */
+ range_deserialize(range, &lower, &upper, &empty);
+
+ if (lower.rngtypid != upper.rngtypid)
+ elog(ERROR, "range types do not match");
+
+ subtype = get_range_subtype(lower.rngtypid);
+
+ 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(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);
+ }
+
+ Datum
+ range_recv(PG_FUNCTION_ARGS)
+ {
+ PG_RETURN_VOID();
+ }
+
+ Datum
+ range_send(PG_FUNCTION_ARGS)
+ {
+ PG_RETURN_VOID();
+ }
+
+
+ /*
+ *----------------------------------------------------------
+ * GENERIC FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+
+ /* constructors */
+
+ Datum
+ range_make1(PG_FUNCTION_ARGS)
+ {
+ Datum arg = PG_GETARG_DATUM(0);
+ Oid subtype = get_fn_expr_argtype(fcinfo->flinfo,0);
+ Oid rngtypid = get_range_from_subtype(subtype);
+ RangeType *range;
+ RangeBound lower;
+ RangeBound upper;
+
+ if (PG_ARGISNULL(0))
+ elog(ERROR, "NULL range boundaries are not supported");
+
+ if (!OidIsValid(subtype) || !OidIsValid(rngtypid))
+ elog(ERROR, "cannot determine argument types");
+
+ lower.rngtypid = rngtypid;
+ lower.inclusive = true;
+ lower.val = arg;
+ lower.lower = true;
+ lower.infinite = false;
+ upper.rngtypid = rngtypid;
+ upper.inclusive = true;
+ upper.val = arg;
+ upper.lower = false;
+ upper.infinite = false;
+
+ range = DatumGetRangeType(make_range(&lower, &upper, false));
+
+ PG_RETURN_RANGE(range);
+ }
+
+ Datum
+ range_linf_(PG_FUNCTION_ARGS)
+ {
+ Datum arg = PG_GETARG_DATUM(0);
+ Oid subtype = get_fn_expr_argtype(fcinfo->flinfo,0);
+ Oid rngtypid = get_range_from_subtype(subtype);
+ RangeType *range;
+ RangeBound lower;
+ RangeBound upper;
+
+ if (PG_ARGISNULL(0))
+ elog(ERROR, "NULL range boundaries are not supported");
+
+ lower.rngtypid = rngtypid;
+ lower.inclusive = false;
+ lower.infinite = true;
+ lower.lower = true;
+ lower.val = (Datum) 0;
+
+ upper.rngtypid = rngtypid;
+ upper.inclusive = false;
+ upper.infinite = false;
+ upper.lower = false;
+ upper.val = arg;
+
+ range = DatumGetRangeType(make_range(&lower, &upper, false));
+
+ PG_RETURN_RANGE(range);
+ }
+
+ Datum
+ range_uinf_(PG_FUNCTION_ARGS)
+ {
+ Datum arg = PG_GETARG_DATUM(0);
+ Oid subtype = get_fn_expr_argtype(fcinfo->flinfo,0);
+ Oid rngtypid = get_range_from_subtype(subtype);
+ RangeType *range;
+ RangeBound lower;
+ RangeBound upper;
+
+ if (PG_ARGISNULL(0))
+ elog(ERROR, "NULL range boundaries are not supported");
+
+ lower.rngtypid = rngtypid;
+ lower.inclusive = false;
+ lower.infinite = false;
+ lower.lower = true;
+ lower.val = arg;
+
+ upper.rngtypid = rngtypid;
+ upper.inclusive = false;
+ upper.infinite = true;
+ upper.lower = false;
+ upper.val = (Datum) 0;
+
+ range = DatumGetRangeType(make_range(&lower, &upper, false));
+
+ PG_RETURN_RANGE(range);
+ }
+
+ Datum
+ range_linfi(PG_FUNCTION_ARGS)
+ {
+ Datum arg = PG_GETARG_DATUM(0);
+ Oid subtype = get_fn_expr_argtype(fcinfo->flinfo,0);
+ Oid rngtypid = get_range_from_subtype(subtype);
+ RangeType *range;
+ RangeBound lower;
+ RangeBound upper;
+
+ if (PG_ARGISNULL(0))
+ elog(ERROR, "NULL range boundaries are not supported");
+
+ lower.rngtypid = rngtypid;
+ lower.inclusive = false;
+ lower.infinite = true;
+ lower.lower = true;
+ lower.val = (Datum) 0;
+
+ upper.rngtypid = rngtypid;
+ upper.inclusive = true;
+ upper.infinite = false;
+ upper.lower = false;
+ upper.val = arg;
+
+ range = DatumGetRangeType(make_range(&lower, &upper, false));
+
+ PG_RETURN_RANGE(range);
+ }
+
+ Datum
+ range_uinfi(PG_FUNCTION_ARGS)
+ {
+ Datum arg = PG_GETARG_DATUM(0);
+ Oid subtype = get_fn_expr_argtype(fcinfo->flinfo,0);
+ Oid rngtypid = get_range_from_subtype(subtype);
+ RangeType *range;
+ RangeBound lower;
+ RangeBound upper;
+
+ if (PG_ARGISNULL(0))
+ elog(ERROR, "NULL range boundaries are not supported");
+
+ lower.rngtypid = rngtypid;
+ lower.inclusive = true;
+ lower.infinite = false;
+ lower.lower = true;
+ lower.val = arg;
+
+ upper.rngtypid = rngtypid;
+ upper.inclusive = false;
+ upper.infinite = true;
+ upper.lower = false;
+ upper.val = (Datum) 0;
+
+ range = DatumGetRangeType(make_range(&lower, &upper, false));
+
+ PG_RETURN_RANGE(range);
+ }
+
+ Datum
+ range(PG_FUNCTION_ARGS)
+ {
+ return range_make2(fcinfo);
+ }
+
+ Datum
+ range__(PG_FUNCTION_ARGS)
+ {
+ return range_make2(fcinfo);
+ }
+
+ Datum
+ range_i(PG_FUNCTION_ARGS)
+ {
+ return range_make2(fcinfo);
+ }
+
+ Datum
+ rangei_(PG_FUNCTION_ARGS)
+ {
+ return range_make2(fcinfo);
+ }
+
+ Datum
+ rangeii(PG_FUNCTION_ARGS)
+ {
+ return range_make2(fcinfo);
+ }
+
+ /* range -> subtype */
+ Datum
+ range_lower(PG_FUNCTION_ARGS)
+ {
+ RangeType *r1 = PG_GETARG_RANGE(0);
+ RangeBound lower;
+ RangeBound upper;
+ bool empty;
+
+ range_deserialize(r1, &lower, &upper, &empty);
+
+ if (empty)
+ elog(ERROR, "range is empty");
+ if (lower.infinite)
+ elog(ERROR, "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(r1, &lower, &upper, &empty);
+
+ if (empty)
+ elog(ERROR, "range is empty");
+ if (upper.infinite)
+ elog(ERROR, "range lower 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(r1, &lower, &upper, &empty);
+
+ PG_RETURN_BOOL(empty);
+ }
+
+ Datum
+ range_non_empty(PG_FUNCTION_ARGS)
+ {
+ RangeType *r1 = PG_GETARG_RANGE(0);
+ RangeBound lower;
+ RangeBound upper;
+ bool empty;
+
+ range_deserialize(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(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(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(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(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(r1, &lower1, &upper1, &empty1);
+ range_deserialize(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(&lower1, &lower2) != 0)
+ PG_RETURN_BOOL(false);
+
+ if (range_cmp_bounds(&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(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(&lower2, &upper2, false));
+
+ PG_RETURN_BOOL(range_contains_internal(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(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(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(&lower2, &upper2, false));
+
+ PG_RETURN_BOOL(range_contains_internal(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(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(r1, &lower1, &upper1, &empty1);
+ range_deserialize(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)
+ elog(ERROR, "empty range");
+
+ if (range_cmp_bounds(&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(r1, &lower1, &upper1, &empty1);
+ range_deserialize(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)
+ elog(ERROR, "empty range");
+
+ if (range_cmp_bounds(&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);
+
+ RangeBound lower1, lower2;
+ RangeBound upper1, upper2;
+ bool empty1, empty2;
+
+ FmgrInfo flinfo;
+ Oid collation;
+
+ range_deserialize(r1, &lower1, &upper1, &empty1);
+ range_deserialize(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)
+ elog(ERROR, "empty range");
+
+ /*
+ * 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_subtype_cmpfn(lower1.rngtypid, &flinfo, &collation);
+
+ if (lower1.inclusive != upper2.inclusive)
+ {
+ if (DatumGetInt32(FunctionCall2Coll(&flinfo, collation,
+ lower1.val, upper2.val)) == 0)
+ PG_RETURN_BOOL(true);
+ }
+
+ if (upper1.inclusive != lower2.inclusive)
+ {
+ if (DatumGetInt32(FunctionCall2Coll(&flinfo, 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(r1, &lower1, &upper1, &empty1);
+ range_deserialize(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(&lower1, &lower2) >= 0 &&
+ range_cmp_bounds(&lower1, &upper2) <= 0)
+ PG_RETURN_BOOL(true);
+
+ if (range_cmp_bounds(&lower2, &lower1) >= 0 &&
+ range_cmp_bounds(&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(r1, &lower1, &upper1, &empty1);
+ range_deserialize(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(&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(r1, &lower1, &upper1, &empty1);
+ range_deserialize(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(&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(r1, &lower1, &upper1, &empty1);
+ range_deserialize(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(&lower1, &lower2);
+ cmp_l1u2 = range_cmp_bounds(&lower1, &upper2);
+ cmp_u1l2 = range_cmp_bounds(&upper1, &lower2);
+ cmp_u1u2 = range_cmp_bounds(&upper1, &upper2);
+
+ if (cmp_l1l2 < 0 && cmp_u1u2 > 0)
+ elog(ERROR, "range_minus resulted in two ranges");
+
+ if (cmp_l1u2 > 0 || cmp_u1l2 < 0)
+ PG_RETURN_RANGE(r1);
+
+ if (cmp_l1l2 >= 0 && cmp_u1u2 <= 0)
+ PG_RETURN_RANGE(make_empty_range(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(&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(&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(r1, &lower1, &upper1, &empty1);
+ range_deserialize(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)))
+ elog(ERROR, "range union resulted in two ranges");
+
+ if (range_cmp_bounds(&lower1, &lower2) < 0)
+ result_lower = &lower1;
+ else
+ result_lower = &lower2;
+
+ if (range_cmp_bounds(&upper1, &upper2) > 0)
+ result_upper = &upper1;
+ else
+ result_upper = &upper2;
+
+ PG_RETURN_RANGE(make_range(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(r1, &lower1, &upper1, &empty1);
+ range_deserialize(r2, &lower2, &upper2, &empty2);
+
+ if (empty1 || empty2 || !DatumGetBool(range_overlaps(fcinfo)))
+ PG_RETURN_RANGE(make_empty_range(lower1.rngtypid));
+
+ if (range_cmp_bounds(&lower1, &lower2) >= 0)
+ result_lower = &lower1;
+ else
+ result_lower = &lower2;
+
+ if (range_cmp_bounds(&upper1, &upper2) <= 0)
+ result_upper = &upper1;
+ else
+ result_upper = &upper2;
+
+ PG_RETURN_RANGE(make_range(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(r1, &lower1, &upper1, &empty1);
+ range_deserialize(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(&lower1, &lower2)) != 0)
+ PG_RETURN_INT32(cmp);
+
+ PG_RETURN_INT32(range_cmp_bounds(&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;
+
+ TypeCacheEntry *typentry;
+ Oid subtype;
+ FunctionCallInfoData locfcinfo;
+
+
+ range_deserialize(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;
+
+ subtype = get_range_subtype(lower.rngtypid);
+
+ /*
+ * 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 array element (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 = (result << 1) | (result >> 31);
+ result ^= lower_hash;
+ result = (result << 1) | (result >> 31);
+ result ^= upper_hash;
+
+ PG_RETURN_INT32(result);
+ }
+
+ /*
+ *----------------------------------------------------------
+ * CANONICAL FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+ Datum
+ int4range_canonical(PG_FUNCTION_ARGS)
+ {
+ RangeType *r = PG_GETARG_RANGE(0);
+
+ RangeBound lower;
+ RangeBound upper;
+ bool empty;
+
+ range_deserialize(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(&lower, &upper, false));
+ }
+
+ Datum
+ int8range_canonical(PG_FUNCTION_ARGS)
+ {
+ RangeType *r = PG_GETARG_RANGE(0);
+
+ RangeBound lower;
+ RangeBound upper;
+ bool empty;
+
+ range_deserialize(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(&lower, &upper, false));
+ }
+
+ Datum
+ daterange_canonical(PG_FUNCTION_ARGS)
+ {
+ RangeType *r = PG_GETARG_RANGE(0);
+
+ RangeBound lower;
+ RangeBound upper;
+ bool empty;
+
+ range_deserialize(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(&lower, &upper, false));
+ }
+
+ /*
+ *----------------------------------------------------------
+ * SUPPORT FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+ /*
+ * 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(RangeBound *lower, RangeBound *upper, bool empty)
+ {
+ Datum range;
+ size_t msize;
+ Pointer ptr;
+ Oid subtype = get_range_subtype(lower->rngtypid);
+ int16 typlen = get_typlen(subtype);
+ char typalign = get_typalign(subtype);
+ char typbyval = get_typbyval(subtype);
+ char typstorage = get_typstorage(subtype);
+ char flags = 0;
+
+ if (lower->rngtypid != upper->rngtypid)
+ elog(ERROR, "range types do not match");
+
+ if (empty)
+ flags |= RANGE_EMPTY;
+ else if (range_cmp_bounds(lower, upper) > 0)
+ elog(ERROR, "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(RangeType *range, RangeBound *lower, RangeBound *upper,
+ bool *empty)
+ {
+ Pointer ptr = VARDATA(range);
+ char typalign;
+ int16 typlen;
+ int16 typbyval;
+ char flags;
+ Oid rngtypid;
+ Oid subtype;
+ Datum lbound;
+ Datum ubound;
+ Pointer flags_ptr;
+
+ 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")));
+
+ subtype = get_range_subtype(rngtypid);
+ typalign = get_typalign(subtype);
+ typlen = get_typlen(subtype);
+ typbyval = get_typbyval(subtype);
+
+ 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(RangeBound *lower, RangeBound *upper, bool empty)
+ {
+ Datum range;
+ Oid canonical = get_range_canonical(lower->rngtypid);
+
+ if (lower->rngtypid != upper->rngtypid)
+ elog(ERROR, "range types do not match");
+
+ range = range_serialize(lower, upper, empty);
+
+ if (OidIsValid(canonical))
+ range = OidFunctionCall1(canonical, range);
+
+ PG_RETURN_RANGE(range);
+ }
+
+ int
+ range_cmp_bounds(RangeBound *b1, RangeBound *b2)
+ {
+ int result;
+ FmgrInfo flinfo;
+ Oid collation;
+
+ 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_subtype_cmpfn(b1->rngtypid, &flinfo, &collation);
+ result = DatumGetInt32(FunctionCall2Coll(&flinfo, 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(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(&lower, &upper, true));
+ }
+
+ /*
+ *----------------------------------------------------------
+ * STATIC FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+ /*
+ * Parse input of the form:
+ *
+ * where
+ * is either "[" or "("
+ * and are either plain strings or quoted strings
+ * is ","
+ * is either "]" or ")"
+ *
+ * Quoted strings preserve whitespace, and allow using special
+ * characters like "," and special values like "INF" ("\" is the
+ * escape character if you need to include a double quote or backslash
+ * character). Unquoted strings preserve internal whitespace, but not
+ * leading or trailing whitespace.
+ */
+ static void
+ range_parse(const char *input_str, char *flags, char **lbound_str,
+ char **ubound_str)
+ {
+ int ilen = strlen(input_str);
+ char *lb = palloc0(ilen + 1);
+ char *ub = palloc0(ilen + 1);
+ int lidx = 0;
+ int lb_last = 1;
+ int uidx = 0;
+ int ub_last = 1;
+ bool inside_quotes = false;
+ bool lb_quoted = false;
+ bool ub_quoted = false;
+ bool escape = false;
+ char fl = 0;
+ RangePState pstate = RANGE_PSTATE_INIT;
+ int i;
+
+
+ for (i = 0; i < ilen; i++)
+ {
+ char ch = input_str[i];
+ switch(pstate)
+ {
+ case RANGE_PSTATE_INIT:
+ if (isspace(ch))
+ continue;
+ if(ch == '-')
+ {
+ fl = RANGE_EMPTY;
+ pstate = RANGE_PSTATE_DONE;
+ /* read the rest to make sure it's whitespace */
+ continue;
+ }
+
+ if (ch == '[' || ch == '(')
+ {
+ if (ch == '[')
+ fl |= RANGE_LB_INC;
+ pstate = RANGE_PSTATE_PRE_LB;
+ continue;
+ }
+ break;
+ case RANGE_PSTATE_PRE_LB:
+ /* remove leading whitespace */
+ if (isspace(ch))
+ continue;
+ pstate = RANGE_PSTATE_LB;
+ /* fall through */
+ case RANGE_PSTATE_LB:
+ if (ch == '"' && !escape)
+ {
+ if (inside_quotes)
+ {
+ inside_quotes = false;
+ pstate = RANGE_PSTATE_SEP;
+ }
+ else
+ {
+ inside_quotes = true;
+ lb_quoted = true;
+ }
+ continue;
+ }
+ if (ch == '\\' && !escape && inside_quotes)
+ {
+ escape = true;
+ continue;
+ }
+ escape = false;
+
+ if (ch != ',')
+ {
+ lb[lidx++] = ch;
+ /* track last significant character */
+ if (inside_quotes || !isspace(ch))
+ lb_last = lidx;
+ continue;
+ }
+
+ pstate = RANGE_PSTATE_SEP;
+ /* fall through */
+ case RANGE_PSTATE_SEP:
+ if (isspace(ch))
+ continue;
+ if (ch == ',')
+ {
+ pstate = RANGE_PSTATE_PRE_UB;
+ continue;
+ }
+ case RANGE_PSTATE_PRE_UB:
+ /* remove leading whitespace */
+ if (isspace(ch))
+ continue;
+ pstate = RANGE_PSTATE_UB;
+ /* fall through */
+ case RANGE_PSTATE_UB:
+ if (ch == '"' && !escape)
+ {
+ if (inside_quotes)
+ {
+ inside_quotes = false;
+ pstate = RANGE_PSTATE_UB_INC;
+ }
+ else
+ {
+ inside_quotes = true;
+ lb_quoted = true;
+ }
+ continue;
+ }
+ if (ch == '\\' && !escape && inside_quotes)
+ {
+ escape = true;
+ continue;
+ }
+ escape = false;
+
+ if (ch != ']' && ch != ')')
+ {
+ ub[uidx++] = ch;
+ /* track last significant character */
+ if (inside_quotes || !isspace(ch))
+ ub_last = uidx;
+ continue;
+ }
+
+ pstate = RANGE_PSTATE_UB_INC;
+ /* fall through */
+ case RANGE_PSTATE_UB_INC:
+ if (isspace(ch))
+ continue;
+ if (ch == ']' || ch == ')')
+ {
+ if (ch == ']')
+ fl |= RANGE_UB_INC;
+ pstate = RANGE_PSTATE_DONE;
+ continue;
+ }
+ break;
+ case RANGE_PSTATE_DONE:
+ if (isspace(ch))
+ continue;
+ break;
+ }
+ elog(ERROR, "syntax error on range input \"%s\", character %d",
+ input_str, i);
+ }
+
+ /* remove trailing whitespace */
+ lb[lb_last] = '\0';
+ ub[ub_last] = '\0';
+
+ if (pstate != RANGE_PSTATE_DONE)
+ elog(ERROR, "syntax error on range input: unexpected end of input");
+
+ if (fl & RANGE_EMPTY)
+ fl = RANGE_EMPTY;
+
+ if (!lb_quoted && strncmp(lb, "NULL", ilen) == 0)
+ elog(ERROR, "NULL range boundaries are not supported");
+ if (!ub_quoted && strncmp(ub, "NULL", ilen) == 0)
+ elog(ERROR, "NULL range boundaries are not supported");
+ if (!lb_quoted && strncmp(lb, "-INF", ilen) == 0)
+ fl |= RANGE_LB_INF;
+ if (!ub_quoted && strncmp(ub, "INF", ilen) == 0)
+ fl |= RANGE_UB_INF;
+
+ if (!RANGE_HAS_LBOUND(fl))
+ {
+ lb = NULL;
+ fl &= ~RANGE_LB_INC;
+ }
+
+ if (!RANGE_HAS_UBOUND(fl))
+ {
+ ub = NULL;
+ fl &= ~RANGE_UB_INC;
+ }
+
+ *lbound_str = lb;
+ *ubound_str = ub;
+ *flags = fl;
+
+ return;
+ }
+
+ static char *
+ range_deparse(char flags, char *lbound_str, char *ubound_str)
+ {
+ StringInfo str = makeStringInfo();
+ char lb_c;
+ char ub_c;
+ char *lb_str;
+ char *ub_str;
+
+ if (flags & RANGE_EMPTY)
+ return pstrdup("-");
+
+ lb_c = (flags & RANGE_LB_INC) ? '[' : '(';
+ ub_c = (flags & RANGE_UB_INC) ? ']' : ')';
+
+ lb_str = lbound_str;
+ ub_str = ubound_str;
+
+ if (!RANGE_HAS_LBOUND(flags))
+ lb_str = (flags & RANGE_LB_NULL) ? "NULL" : "-INF";
+ if (!RANGE_HAS_UBOUND(flags))
+ ub_str = (flags & RANGE_UB_NULL) ? "NULL" : "INF";
+
+ appendStringInfo(str, "%c %s, %s %c", lb_c, lb_str, ub_str, ub_c);
+
+ return str->data;
+ }
+
+ static Datum
+ range_make2(PG_FUNCTION_ARGS)
+ {
+ Datum arg1 = PG_GETARG_DATUM(0);
+ Datum arg2 = PG_GETARG_DATUM(1);
+ Oid subtype = get_fn_expr_argtype(fcinfo->flinfo,0);
+ Oid rngtypid = get_range_from_subtype(subtype);
+ RangeType *range;
+ RangeBound lower;
+ RangeBound upper;
+
+ memset(&lower, 0, sizeof(RangeBound));
+ memset(&upper, 0, sizeof(RangeBound));
+
+ switch(fcinfo->flinfo->fn_oid)
+ {
+ case F_RANGE__:
+ break;
+ case F_RANGE_I:
+ upper.inclusive = true;
+ break;
+ case F_RANGE:
+ case F_RANGEI_:
+ lower.inclusive = true;
+ break;
+ case F_RANGEII:
+ lower.inclusive = true;
+ upper.inclusive = true;
+ break;
+ }
+
+ if (PG_ARGISNULL(0) || PG_ARGISNULL(1))
+ elog(ERROR, "NULL range boundaries are not supported");
+
+ lower.rngtypid = rngtypid;
+ lower.lower = true;
+ lower.val = arg1;
+
+ upper.rngtypid = rngtypid;
+ upper.lower = false;
+ upper.val = arg2;
+
+ range = DatumGetRangeType(make_range(&lower, &upper, false));
+
+ PG_RETURN_RANGE(range);
+ }
+
+ static bool
+ range_contains_internal(RangeType *r1, RangeType *r2)
+ {
+ RangeBound lower1;
+ RangeBound upper1;
+ bool empty1;
+ RangeBound lower2;
+ RangeBound upper2;
+ bool empty2;
+
+ range_deserialize(r1, &lower1, &upper1, &empty1);
+ range_deserialize(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(&lower1, &lower2) > 0)
+ return false;
+ if (range_cmp_bounds(&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;
+ }
+
+ 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;
+ }
+
+ static void
+ range_subtype_cmpfn(Oid rngtypid, FmgrInfo *flinfo, Oid *collation)
+ {
+ Oid opclassId = get_range_sub_opclass(rngtypid);
+ Oid opfamilyId;
+ Oid cmpFnOid;
+ Oid subtype;
+ HeapTuple tuple;
+ Form_pg_opclass pg_opclass;
+
+ *collation = get_range_collation(rngtypid);
+
+ tuple = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassId));
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("operator class with OID %u does not exist",
+ opclassId)));
+ pg_opclass = (Form_pg_opclass) GETSTRUCT(tuple);
+ opfamilyId = pg_opclass->opcfamily;
+ ReleaseSysCache(tuple);
+
+ subtype = getBaseType(get_range_subtype(rngtypid));
+ cmpFnOid = get_opfamily_proc(opfamilyId, subtype, subtype, BTORDER_PROC);
+
+ fmgr_info(cmpFnOid, flinfo);
+ }
*** /dev/null
--- b/src/backend/utils/adt/rangetypes_gist.c
***************
*** 0 ****
--- 1,537 ----
+ /*-------------------------------------------------------------------------
+ *
+ * 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/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(RangeType *r1, RangeType *r2);
+ static bool range_gist_consistent_int(StrategyNumber strategy, RangeType *key,
+ RangeType *query);
+ static bool range_gist_consistent_leaf(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;
+ } PickSplitSortItem;
+
+
+ /* GiST support */
+ 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(key, &lower, &upper, &empty);
+ rngtypid = lower.rngtypid;
+
+ switch(strategy)
+ {
+ RangeBound lower;
+ RangeBound upper;
+
+ 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(&lower, &upper, false));
+ break;
+ default:
+ query = DatumGetRangeType(dquery);
+ break;
+ }
+
+ if(GIST_LEAF(entry))
+ PG_RETURN_BOOL(range_gist_consistent_leaf(strategy, key, query));
+ else
+ PG_RETURN_BOOL(range_gist_consistent_int(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(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(orig, new);
+
+ regproc subtype_float;
+ RangeBound lower1, lower2;
+ RangeBound upper1, upper2;
+ bool empty1, empty2;
+ float length1, length2;
+
+ range_deserialize(orig, &lower1, &upper1, &empty1);
+ range_deserialize(s_union, &lower2, &upper2, &empty2);
+
+ subtype_float = get_range_subtype_float(lower1.rngtypid);
+
+ if (empty1)
+ length1 = 0.0;
+ else if (lower1.infinite || upper1.infinite)
+ length1 = 1.0/0.0;
+ else if (OidIsValid(subtype_float))
+ {
+ double l = DatumGetFloat8(OidFunctionCall1(subtype_float, lower1.val));
+ double u = DatumGetFloat8(OidFunctionCall1(subtype_float, upper1.val));
+ length1 = u - l;
+ }
+ else
+ length1 = 1.0;
+
+ if (empty2)
+ length2 = 0.0;
+ else if (lower2.infinite || upper2.infinite)
+ length2 = 1.0/0.0;
+ else if (OidIsValid(subtype_float))
+ {
+ double l = DatumGetFloat8(OidFunctionCall1(subtype_float, lower2.val));
+ double u = DatumGetFloat8(OidFunctionCall1(subtype_float, upper2.val));
+ length2 = u - l;
+ }
+ else
+ length2 = 1.0;
+
+ *penalty = (float) (length2 - length1);
+ PG_RETURN_POINTER(penalty);
+ }
+
+ /*
+ * The GiST PickSplit method for ranges
+ * Algorithm based on sorting. Incoming array of periods is sorting using
+ * period_compare 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);
+ }
+ 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(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(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(DirectFunctionCall2(range_eq, r1, r2));
+ PG_RETURN_POINTER(result);
+ }
+
+ /*
+ *----------------------------------------------------------
+ * STATIC FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+ /* return the smallest range that contains r1 and r2 */
+ static RangeType *
+ range_super_union(RangeType *r1, RangeType *r2)
+ {
+ RangeBound lower1, lower2;
+ RangeBound upper1, upper2;
+ bool empty1, empty2;
+ RangeBound *result_lower;
+ RangeBound *result_upper;
+
+ range_deserialize(r1, &lower1, &upper1, &empty1);
+ range_deserialize(r2, &lower2, &upper2, &empty2);
+
+ if (empty1)
+ return r2;
+ if (empty2)
+ return r1;
+
+ if (range_cmp_bounds(&lower1, &lower2) <= 0)
+ result_lower = &lower1;
+ else
+ result_lower = &lower2;
+
+ if (range_cmp_bounds(&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(result_lower, result_upper, false));
+ }
+
+ static bool
+ range_gist_consistent_int(StrategyNumber strategy, RangeType *key,
+ RangeType *query)
+ {
+ Datum (*proc)(PG_FUNCTION_ARGS) = NULL;
+
+ RangeBound lower1, lower2;
+ RangeBound upper1, upper2;
+ bool empty1, empty2;
+
+ bool retval;
+ bool negate = false;
+
+ range_deserialize(key, &lower1, &upper1, &empty1);
+ range_deserialize(query, &lower2, &upper2, &empty2);
+
+ switch (strategy)
+ {
+ case RANGESTRAT_EQ:
+ proc = range_contains;
+ break;
+ case RANGESTRAT_NE:
+ return true;
+ break;
+ case RANGESTRAT_OVERLAPS:
+ proc = range_overlaps;
+ break;
+ case RANGESTRAT_CONTAINS_ELEM:
+ case RANGESTRAT_CONTAINS:
+ proc = range_contains;
+ break;
+ case RANGESTRAT_ELEM_CONTAINED_BY:
+ case RANGESTRAT_CONTAINED_BY:
+ return true;
+ break;
+ case RANGESTRAT_BEFORE:
+ if (empty1)
+ return false;
+ proc = range_overright;
+ negate = true;
+ break;
+ case RANGESTRAT_AFTER:
+ if (empty1)
+ return false;
+ proc = range_overleft;
+ negate = true;
+ break;
+ case RANGESTRAT_OVERLEFT:
+ if (empty1)
+ return false;
+ proc = range_after;
+ negate = true;
+ break;
+ case RANGESTRAT_OVERRIGHT:
+ if (empty1)
+ return false;
+ proc = range_before;
+ negate = true;
+ break;
+ case RANGESTRAT_ADJACENT:
+ if (empty1 || empty2)
+ return false;
+ if (DatumGetBool(
+ DirectFunctionCall2(range_adjacent,
+ RangeTypeGetDatum(key),
+ RangeTypeGetDatum(query))))
+ return true;
+ proc = range_overlaps;
+ break;
+ }
+
+ retval = DatumGetBool(DirectFunctionCall2(proc, RangeTypeGetDatum(key),
+ RangeTypeGetDatum(query)));
+
+ if (negate)
+ retval = !retval;
+
+ PG_RETURN_BOOL(retval);
+ }
+
+ static bool
+ range_gist_consistent_leaf(StrategyNumber strategy, RangeType *key,
+ RangeType *query)
+ {
+ Datum (*proc)(PG_FUNCTION_ARGS) = NULL;
+
+ RangeBound lower1, lower2;
+ RangeBound upper1, upper2;
+ bool empty1, empty2;
+
+ range_deserialize(key, &lower1, &upper1, &empty1);
+ range_deserialize(query, &lower2, &upper2, &empty2);
+
+ switch (strategy)
+ {
+ case RANGESTRAT_EQ:
+ proc = range_eq;
+ break;
+ case RANGESTRAT_NE:
+ proc = range_ne;
+ break;
+ case RANGESTRAT_OVERLAPS:
+ proc = range_overlaps;
+ break;
+ case RANGESTRAT_CONTAINS_ELEM:
+ case RANGESTRAT_CONTAINS:
+ proc = range_contains;
+ break;
+ case RANGESTRAT_ELEM_CONTAINED_BY:
+ case RANGESTRAT_CONTAINED_BY:
+ proc = range_contained_by;
+ break;
+ case RANGESTRAT_BEFORE:
+ if (empty1 || empty2)
+ return false;
+ proc = range_before;
+ break;
+ case RANGESTRAT_AFTER:
+ if (empty1 || empty2)
+ return false;
+ proc = range_after;
+ break;
+ case RANGESTRAT_OVERLEFT:
+ if (empty1 || empty2)
+ return false;
+ proc = range_overleft;
+ break;
+ case RANGESTRAT_OVERRIGHT:
+ if (empty1 || empty2)
+ return false;
+ proc = range_overright;
+ break;
+ case RANGESTRAT_ADJACENT:
+ if (empty1 || empty2)
+ return false;
+ proc = range_adjacent;
+ break;
+ }
+
+ return DatumGetBool(DirectFunctionCall2(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;
+
+ int cmp;
+
+ range_deserialize(r1, &lower1, &upper1, &empty1);
+ range_deserialize(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(&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(&lower1, &lower2);
+ else if (upper1.infinite)
+ return 1;
+ else if (upper2.infinite)
+ return -1;
+ else
+ Assert(false);
+ }
+
+ if ((cmp = range_cmp_bounds(&lower1, &lower2)) != 0)
+ return cmp;
+
+ return range_cmp_bounds(&upper1, &upper2);
+ }
*** a/src/backend/utils/adt/timestamp.c
--- b/src/backend/utils/adt/timestamp.c
***************
*** 4630,4635 **** timestamptz_zone(PG_FUNCTION_ARGS)
--- 4630,4650 ----
PG_RETURN_TIMESTAMP(result);
}
+ Datum
+ timestamp_float8(PG_FUNCTION_ARGS)
+ {
+ Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
+ float8 result;
+
+ #ifdef HAVE_INT64_TIMESTAMP
+ result = ((float8) timestamp) / USECS_PER_SEC;
+ #else
+ result = timestamp;
+ #endif
+
+ PG_RETURN_FLOAT8(result);
+ }
+
/* timestamptz_izone()
* Encode timestamp with time zone type with specified time interval as time zone.
* Returns a timestamp without time zone.
*** a/src/backend/utils/cache/lsyscache.c
--- b/src/backend/utils/cache/lsyscache.c
***************
*** 26,31 ****
--- 26,32 ----
#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"
***************
*** 1978,1984 **** get_type_io_data(Oid typid,
ReleaseSysCache(typeTuple);
}
- #ifdef NOT_USED
char
get_typalign(Oid typid)
{
--- 1979,1984 ----
***************
*** 1997,2003 **** get_typalign(Oid typid)
else
return 'i';
}
- #endif
char
get_typstorage(Oid typid)
--- 1997,2002 ----
***************
*** 2251,2256 **** type_is_enum(Oid typid)
--- 2250,2265 ----
}
/*
+ * 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
*
* Given the type OID, fetch its category and preferred-type status.
***************
*** 2855,2857 **** get_namespace_name(Oid nspid)
--- 2864,2980 ----
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;
+ }
+
+ Oid
+ get_range_collation(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->rngcollation;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+ }
+
+ Oid
+ get_range_from_subtype(Oid subtypeOid)
+ {
+ HeapTuple tp;
+
+ tp = SearchSysCache1(RANGESUBTYPE, ObjectIdGetDatum(subtypeOid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp);
+ Oid result;
+
+ result = rngtup->rngtypid;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+ }
+
+ Oid
+ get_range_sub_opclass(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->rngsubopc;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+ }
+
+ Oid
+ get_range_subtype_float(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->rngsubfloat;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+ }
+
+ Oid
+ get_range_canonical(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->rngcanonical;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+ }
*** a/src/backend/utils/cache/syscache.c
--- b/src/backend/utils/cache/syscache.c
***************
*** 43,48 ****
--- 43,49 ----
#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,559 **** static const struct cachedesc cacheinfo[] = {
--- 555,582 ----
},
2048
},
+ {RangeRelationId, /* RANGETYPE */
+ RangeTypidIndexId,
+ 1,
+ {
+ Anum_pg_range_rngtypid,
+ 0,
+ 0,
+ 0
+ },
+ 1024
+ },
+ {RangeRelationId, /* RANGESUBTYPE */
+ RangeSubtypeIndexId,
+ 1,
+ {
+ Anum_pg_range_rngsubtype,
+ 0,
+ 0,
+ 0
+ },
+ 1024
+ },
{RelationRelationId, /* RELNAMENSP */
ClassNameNspIndexId,
2,
*** a/src/backend/utils/fmgr/funcapi.c
--- b/src/backend/utils/fmgr/funcapi.c
***************
*** 408,417 **** resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
--- 408,419 ----
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 anyrange_type = InvalidOid;
Oid anycollation;
int i;
***************
*** 434,439 **** resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
--- 436,443 ----
have_anyelement_result = true;
have_anyenum = true;
break;
+ case ANYRANGEOID:
+ have_anyrange_result = true;
default:
break;
}
***************
*** 462,467 **** resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
--- 466,475 ----
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;
}
***************
*** 481,486 **** resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
--- 489,503 ----
anyelement_type,
ANYELEMENTOID);
+ if (have_anyelement_result && !OidIsValid(anyelement_type))
+ anyelement_type = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+ if (have_anyrange_result && !OidIsValid(anyrange_type))
+ anyrange_type = resolve_generic_type(ANYRANGEOID,
+ anyelement_type,
+ ANYELEMENTOID);
+
/* Enforce ANYNONARRAY if needed */
if (have_anynonarray && type_is_array(anyelement_type))
return false;
***************
*** 530,535 **** resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
--- 547,559 ----
0);
TupleDescInitEntryCollation(tupdesc, i + 1, anycollation);
break;
+ case ANYRANGEOID:
+ TupleDescInitEntry(tupdesc, i + 1,
+ NameStr(tupdesc->attrs[i]->attname),
+ anyrange_type,
+ -1,
+ 0);
+ break;
default:
break;
}
***************
*** 569,574 **** resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
--- 593,599 ----
case ANYELEMENTOID:
case ANYNONARRAYOID:
case ANYENUMOID:
+ case ANYRANGEOID:
if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
have_anyelement_result = true;
else
***************
*** 633,638 **** resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
--- 658,664 ----
case ANYELEMENTOID:
case ANYNONARRAYOID:
case ANYENUMOID:
+ case ANYRANGEOID:
argtypes[i] = anyelement_type;
break;
case ANYARRAYOID:
*** a/src/bin/pg_dump/pg_dump.c
--- b/src/bin/pg_dump/pg_dump.c
***************
*** 167,172 **** static void dumpExtension(Archive *fout, ExtensionInfo *extinfo);
--- 167,173 ----
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);
***************
*** 7297,7302 **** dumpType(Archive *fout, TypeInfo *tyinfo)
--- 7298,7305 ----
dumpCompositeType(fout, tyinfo);
else if (tyinfo->typtype == TYPTYPE_ENUM)
dumpEnumType(fout, tyinfo);
+ else if (tyinfo->typtype == TYPTYPE_RANGE)
+ dumpRangeType(fout, tyinfo);
}
/*
***************
*** 7421,7426 **** dumpEnumType(Archive *fout, TypeInfo *tyinfo)
--- 7424,7512 ----
}
/*
+ * dumpEnumType
+ * writes out to fout the queries to recreate a user-defined enum type
+ */
+ static void
+ dumpRangeType(Archive *fout, TypeInfo *tyinfo)
+ {
+ PQExpBuffer q = createPQExpBuffer();
+ PQExpBuffer delq = createPQExpBuffer();
+ PQExpBuffer query = createPQExpBuffer();
+ PGresult *res;
+ int num;
+
+ char *canonical;
+ Oid canonicalOid;
+
+ /* Set proper schema search path */
+ selectSourceSchema("pg_catalog");
+
+ appendPQExpBuffer(query, "SELECT rngtypid, "
+ "format_type(rngsubtype, NULL) as rngsubtype, "
+ "rngsubcmp, rngcanonical "
+ "FROM pg_catalog.pg_range "
+ "WHERE rngtypid = '%u'",
+ tyinfo->dobj.catId.oid);
+
+ res = PQexec(g_conn, query->data);
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
+
+ num = PQntuples(res);
+
+ /*
+ * 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));
+
+ appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (",
+ fmtId(tyinfo->dobj.name));
+
+ appendPQExpBuffer(q, "\n SUBTYPE = %s",
+ PQgetvalue(res, 0, PQfnumber(res, "rngsubtype")));
+ appendPQExpBuffer(q, ",\n SUBTYPE_CMP = %s",
+ PQgetvalue(res, 0, PQfnumber(res, "rngsubcmp")));
+
+ canonical = PQgetvalue(res, 0, PQfnumber(res, "rngcanonical"));
+ canonicalOid = atooid(canonical);
+ if (OidIsValid(canonicalOid))
+ appendPQExpBuffer(q, ",\n CANONICAL = %s", canonical);
+
+ appendPQExpBuffer(q, "\n);\n");
+
+ 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 */
+ resetPQExpBuffer(q);
+
+ appendPQExpBuffer(q, "TYPE %s", fmtId(tyinfo->dobj.name));
+ dumpComment(fout, q->data,
+ tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
+ tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
+ dumpSecLabel(fout, q->data,
+ tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
+ tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
+
+ PQclear(res);
+ destroyPQExpBuffer(q);
+ destroyPQExpBuffer(delq);
+ destroyPQExpBuffer(query);
+ }
+
+ /*
* dumpBaseType
* writes out to fout the queries to recreate a user-defined base type
*/
*** a/src/include/catalog/catversion.h
--- b/src/include/catalog/catversion.h
***************
*** 53,58 ****
*/
/* yyyymmddN */
! #define CATALOG_VERSION_NO 201108051
#endif
--- 53,59 ----
*/
/* yyyymmddN */
! /* COMMITTER: please set appropriately */
! #define CATALOG_VERSION_NO 201111111
#endif
*** a/src/include/catalog/indexing.h
--- b/src/include/catalog/indexing.h
***************
*** 303,308 **** DECLARE_UNIQUE_INDEX(pg_extension_oid_index, 3080, on pg_extension using btree(o
--- 303,313 ----
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
+ DECLARE_UNIQUE_INDEX(pg_range_rgnsubtype_index, 3543, on pg_range using btree(rngsubtype oid_ops));
+ #define RangeSubtypeIndexId 3543
+
/* last step of initialization script: build the indexes declared above */
BUILD_INDICES
*** a/src/include/catalog/pg_amop.h
--- b/src/include/catalog/pg_amop.h
***************
*** 709,712 **** DATA(insert ( 3683 3615 3615 5 s 3679 403 0 ));
--- 709,742 ----
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 */
*** a/src/include/catalog/pg_amproc.h
--- b/src/include/catalog/pg_amproc.h
***************
*** 336,340 **** DATA(insert ( 3659 3614 3614 4 3658 ));
--- 336,349 ----
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 */
*** a/src/include/catalog/pg_opclass.h
--- b/src/include/catalog/pg_opclass.h
***************
*** 213,217 **** DATA(insert ( 783 tsvector_ops PGNSP PGUID 3655 3614 t 3642 ));
--- 213,220 ----
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 */
*** a/src/include/catalog/pg_operator.h
--- b/src/include/catalog/pg_operator.h
***************
*** 1661,1666 **** DESCR("less than or equal");
--- 1661,1709 ----
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 eqsel eqjoinsel ));
+ DESCR("less than");
+ DATA(insert OID = 3885 ( "<=" PGNSP PGUID b f f 3831 3831 16 3886 3887 range_le eqsel eqjoinsel ));
+ DESCR("less than or equal");
+ DATA(insert OID = 3886 ( ">=" PGNSP PGUID b f f 3831 3831 16 3885 3884 range_ge eqsel eqjoinsel ));
+ DESCR("greater than or equal");
+ DATA(insert OID = 3887 ( ">" PGNSP PGUID b f f 3831 3831 16 3884 3885 range_gt eqsel eqjoinsel ));
+ DESCR("greater than");
+ DATA(insert OID = 3888 ( "&&" PGNSP PGUID b f f 3831 3831 16 3888 0 3857 scalarltsel scalarltjoinsel ));
+ DESCR("overlaps");
+ DATA(insert OID = 3889 ( "@>" PGNSP PGUID b f f 3831 2776 16 3891 0 3858 scalargtsel scalargtjoinsel ));
+ DESCR("contains");
+ DATA(insert OID = 3890 ( "@>" PGNSP PGUID b f f 3831 3831 16 3892 0 3859 scalargtsel scalargtjoinsel ));
+ DESCR("contains");
+ DATA(insert OID = 3891 ( "<@" PGNSP PGUID b f f 2776 3831 16 3889 0 3860 scalargtsel scalargtjoinsel ));
+ DESCR("contained by");
+ DATA(insert OID = 3892 ( "<@" PGNSP PGUID b f f 3831 3831 16 3890 0 3861 scalargtsel scalargtjoinsel ));
+ DESCR("contained by");
+ DATA(insert OID = 3893 ( "<<" PGNSP PGUID b f f 3831 3831 16 0 0 before scalargtsel scalargtjoinsel ));
+ 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 scalargtsel scalargtjoinsel ));
+ 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");
+ DATA(insert OID = 3920 ( "!?" PGNSP PGUID r f f 3831 0 16 0 3921 3850 - - ));
+ DESCR("empty");
+ DATA(insert OID = 3921 ( "?" PGNSP PGUID r f f 3831 0 16 0 3920 3816 - - ));
+ DESCR("non-empty");
/*
* function prototypes
*** a/src/include/catalog/pg_opfamily.h
--- b/src/include/catalog/pg_opfamily.h
***************
*** 139,143 **** DATA(insert OID = 3655 ( 783 tsvector_ops PGNSP PGUID ));
--- 139,146 ----
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 */
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 1216,1221 **** DATA(insert OID = 1180 ( abstime PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 702
--- 1216,1227 ----
DESCR("convert timestamp with time zone to abstime");
DATA(insert OID = 1181 ( age PGNSP PGUID 12 1 0 0 0 f f f t f s 1 0 23 "28" _null_ _null_ _null_ _null_ xid_age _null_ _null_ _null_ ));
DESCR("age of a transaction ID, in transactions before current transaction");
+ DATA(insert OID = 3916 ( float8 PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 701 "1114" _null_ _null_ _null_ _null_ timestamp_float8 _null_ _null_ _null_ ));
+ DESCR("convert timestamp to float8");
+ DATA(insert OID = 3917 ( float8 PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 701 "1184" _null_ _null_ _null_ _null_ timestamp_float8 _null_ _null_ _null_ ));
+ DESCR("convert timestamp with time zone to float8");
+ DATA(insert OID = 3918 ( float8 PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 701 "1082" _null_ _null_ _null_ _null_ date_float8 _null_ _null_ _null_ ));
+ DESCR("convert date to float8");
DATA(insert OID = 1188 ( timestamptz_mi PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 1186 "1184 1184" _null_ _null_ _null_ _null_ timestamp_mi _null_ _null_ _null_ ));
DATA(insert OID = 1189 ( timestamptz_pl_interval PGNSP PGUID 12 1 0 0 0 f f f t f s 2 0 1184 "1184 1186" _null_ _null_ _null_ _null_ timestamptz_pl_interval _null_ _null_ _null_ ));
***************
*** 4331,4336 **** DESCR("fetch the last row value");
--- 4337,4458 ----
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 = 3838 ( range PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 3831 "2283" _null_ _null_ _null_ _null_ range_make1 _null_ _null_ _null_ ));
+ DESCR("construct a range from a single point");
+ DATA(insert OID = 3839 ( range PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 3831 "2283 2283" _null_ _null_ _null_ _null_ range _null_ _null_ _null_ ));
+ DESCR("construct a left-inclusive range from two points");
+ DATA(insert OID = 3840 ( range__ PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 3831 "2283 2283" _null_ _null_ _null_ _null_ range__ _null_ _null_ _null_ ));
+ DESCR("construct an exclusive range from two points");
+ DATA(insert OID = 3841 ( range_i PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 3831 "2283 2283" _null_ _null_ _null_ _null_ range_i _null_ _null_ _null_ ));
+ DESCR("construct a right-inclusive range from two points");
+ DATA(insert OID = 3842 ( rangei_ PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 3831 "2283 2283" _null_ _null_ _null_ _null_ rangei_ _null_ _null_ _null_ ));
+ DESCR("construct a left-inclusive range from two points");
+ DATA(insert OID = 3843 ( rangeii PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 3831 "2283 2283" _null_ _null_ _null_ _null_ rangeii _null_ _null_ _null_ ));
+ DESCR("construct an inclusive range from two points");
+ DATA(insert OID = 3844 ( range_linf_ PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 3831 "2283" _null_ _null_ _null_ _null_ range_linf_ _null_ _null_ _null_ ));
+ DESCR("construct a range with an infinite lower bound and exclusive upper bound");
+ DATA(insert OID = 3845 ( range_uinf_ PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 3831 "2283" _null_ _null_ _null_ _null_ range_uinf_ _null_ _null_ _null_ ));
+ DESCR("construct a range with an infinite upper bound and exclusive lower bound");
+ DATA(insert OID = 3846 ( range_linfi PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 3831 "2283" _null_ _null_ _null_ _null_ range_linfi _null_ _null_ _null_ ));
+ DESCR("construct a range with an infinite lower bound and inclusive upper bound");
+ DATA(insert OID = 3847 ( range_uinfi PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 3831 "2283" _null_ _null_ _null_ _null_ range_uinfi _null_ _null_ _null_ ));
+ DESCR("construct a range with an infinite upper bound and inclusive lower bound");
+ 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 ( empty 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("implementation of !? operator");
+ DATA(insert OID = 3816 ( non_empty PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 16 "3831" _null_ _null_ _null_ _null_ range_non_empty _null_ _null_ _null_ ));
+ DESCR("implementation of ? operator");
+ 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("the set difference of the two ranges");
+ 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("the set difference of the two ranges");
+ 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("the set difference of the two ranges");
+ 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("the set difference of the two ranges");
+ 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("the set difference of the two ranges");
+ 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("the set difference of the two ranges");
+ 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("the set difference of the two ranges");
+ 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 ( length PGNSP PGUID 14 100 0 0 0 f f f t f i 1 0 2283 "3831" _null_ _null_ _null_ _null_ "select pg_catalog.upper($1) - pg_catalog.lower($1);" _null_ _null_ _null_ ));
+ DESCR("range upper bound minus range lower bound");
+ DATA(insert OID = 3923 ( length PGNSP PGUID 14 100 0 0 0 f f f t f i 1 0 1186 "3908" _null_ _null_ _null_ _null_ "select pg_catalog.upper($1) - pg_catalog.lower($1);" _null_ _null_ _null_ ));
+ DESCR("range upper bound minus range lower bound");
+ DATA(insert OID = 3924 ( length PGNSP PGUID 14 100 0 0 0 f f f t f i 1 0 1186 "3910" _null_ _null_ _null_ _null_ "select pg_catalog.upper($1) - pg_catalog.lower($1);" _null_ _null_ _null_ ));
+ DESCR("range upper bound minus range lower bound");
+ DATA(insert OID = 3925 ( length PGNSP PGUID 14 100 0 0 0 f f f t f i 1 0 23 "3912" _null_ _null_ _null_ _null_ "select pg_catalog.upper($1) - pg_catalog.lower($1);" _null_ _null_ _null_ ));
+ DESCR("range upper bound minus range lower bound");
+
/*
* Symbolic values for provolatile column: these indicate whether the result
*** /dev/null
--- b/src/include/catalog/pg_range.h
***************
*** 0 ****
--- 1,82 ----
+ /*-------------------------------------------------------------------------
+ *
+ * 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 */
+ regproc rngsubopc; /* subtype's btree opclass */
+ regproc rngcanonical; /* canonicalize range, or 0 */
+ regproc rngsubfloat; /* represent subtype 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_rngsubfloat 6
+
+ /*
+ * prototypes for functions in pg_range.c
+ */
+
+ void RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
+ Oid rangeSubOpclass, regproc rangeCanonical,
+ regproc rangeSubFloat);
+ void RangeDelete(Oid rangeTypeOid);
+
+ /* ----------------
+ * initial contents of pg_range
+ * ----------------
+ */
+ DATA(insert ( 3904 23 0 1978 int4range_canonical 316 ));
+ DATA(insert ( 3906 1700 0 10037 - 1746 ));
+ DATA(insert ( 3908 1114 0 10054 - 3916 ));
+ DATA(insert ( 3910 1184 0 10047 - 3917 ));
+ DATA(insert ( 3912 1082 0 10019 daterange_canonical 3918 ));
+ DATA(insert ( 3926 20 0 10029 int8range_canonical 482 ));
+
+ #endif /* PG_RANGE_H */
*** a/src/include/catalog/pg_type.h
--- b/src/include/catalog/pg_type.h
***************
*** 591,596 **** DATA(insert OID = 2970 ( txid_snapshot PGNSP PGUID -1 f b U f t \054 0 0 2949 tx
--- 591,623 ----
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_ ));
+ #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_ ));
+ #define INT4RANGEARRAYOID 3905
+ 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_ ));
+ #define NUMRANGEOID 3906
+ 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_ ));
+ #define NUMRANGEARRAYOID 3907
+ 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_ ));
+ #define TSRANGEOID 3908
+ 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_ ));
+ #define TSRANGEARRAYOID 3909
+ 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_ ));
+ #define TSTZRANGEOID 3910
+ 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_ ));
+ #define TSTZRANGEARRAYOID 3911
+ 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_ ));
+ #define DATERANGEOID 3912
+ 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_ ));
+ #define DATERANGEARRAYOID 3913
+ 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_ ));
+ #define INT8RANGEOID 3926
+ 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_ ));
+ #define INT8RANGEARRAYOID 3927
+
/*
* pseudo-types
*
***************
*** 632,637 **** DATA(insert OID = 3500 ( anyenum PGNSP PGUID 4 t p P f t \054 0 0 0 anyenum_in
--- 659,666 ----
#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,647 **** DATA(insert OID = 3115 ( fdw_handler PGNSP PGUID 4 t p P f t \054 0 0 0 fdw_han
--- 671,677 ----
#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,658 **** DATA(insert OID = 3115 ( fdw_handler PGNSP PGUID 4 t p P f t \054 0 0 0 fdw_han
--- 683,689 ----
#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,669 **** 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)
#endif /* PG_TYPE_H */
--- 695,701 ----
((typid) == ANYELEMENTOID || \
(typid) == ANYARRAYOID || \
(typid) == ANYNONARRAYOID || \
! (typid) == ANYENUMOID || \
! (typid) == ANYRANGEOID)
#endif /* PG_TYPE_H */
*** a/src/include/commands/typecmds.h
--- b/src/include/commands/typecmds.h
***************
*** 24,29 **** extern void RemoveTypes(DropStmt *drop);
--- 24,30 ----
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);
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
***************
*** 344,349 **** typedef enum NodeTag
--- 344,350 ----
T_ReassignOwnedStmt,
T_CompositeTypeStmt,
T_CreateEnumStmt,
+ T_CreateRangeStmt,
T_AlterEnumStmt,
T_AlterTSDictionaryStmt,
T_AlterTSConfigurationStmt,
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
***************
*** 2332,2337 **** typedef struct AlterEnumStmt
--- 2332,2348 ----
} AlterEnumStmt;
/* ----------------------
+ * Create Type Statement, enum 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
* ----------------------
*/
*** a/src/include/utils/date.h
--- b/src/include/utils/date.h
***************
*** 146,151 **** extern Datum date_timestamptz(PG_FUNCTION_ARGS);
--- 146,152 ----
extern Datum timestamptz_date(PG_FUNCTION_ARGS);
extern Datum datetime_timestamp(PG_FUNCTION_ARGS);
extern Datum abstime_date(PG_FUNCTION_ARGS);
+ extern Datum date_float8(PG_FUNCTION_ARGS);
extern Datum time_in(PG_FUNCTION_ARGS);
extern Datum time_out(PG_FUNCTION_ARGS);
*** a/src/include/utils/lsyscache.h
--- b/src/include/utils/lsyscache.h
***************
*** 101,106 **** extern Oid get_rel_namespace(Oid relid);
--- 101,107 ----
extern Oid get_rel_type_id(Oid relid);
extern char get_rel_relkind(Oid relid);
extern Oid get_rel_tablespace(Oid relid);
+ extern char get_typalign(Oid typid);
extern bool get_typisdefined(Oid typid);
extern int16 get_typlen(Oid typid);
extern bool get_typbyval(Oid typid);
***************
*** 121,126 **** extern Node *get_typdefault(Oid typid);
--- 122,128 ----
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);
***************
*** 149,154 **** extern void free_attstatsslot(Oid atttype,
--- 151,162 ----
Datum *values, int nvalues,
float4 *numbers, int nnumbers);
extern char *get_namespace_name(Oid nspid);
+ extern Oid get_range_subtype(Oid rangeOid);
+ extern Oid get_range_collation(Oid rangeOid);
+ extern Oid get_range_from_subtype(Oid subtypeOid);
+ extern Oid get_range_sub_opclass(Oid rangeOid);
+ extern Oid get_range_subtype_float(Oid rangeOid);
+ extern Oid get_range_canonical(Oid rangeOid);
#define type_is_array(typid) (get_element_type(typid) != InvalidOid)
/* type_is_array_domain accepts both plain arrays and domains over arrays */
*** /dev/null
--- b/src/include/utils/rangetypes.h
***************
*** 0 ****
--- 1,126 ----
+ /*-------------------------------------------------------------------------
+ *
+ * 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;
+
+ /*
+ * 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_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_non_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);
+
+
+ /* for defining more generic functions */
+ extern Datum make_range(RangeBound *lower, RangeBound *upper, bool empty);
+ extern void range_deserialize(RangeType *range, RangeBound *lower,
+ RangeBound *upper, bool *empty);
+ extern int range_cmp_bounds(RangeBound *b1, RangeBound *b2);
+ extern RangeType *make_empty_range(Oid rngtypid);
+
+ /* for defining a range "canonicalize" function */
+ extern Datum range_serialize(RangeBound *lower, RangeBound *upper, bool empty);
+
+ #endif /* RANGETYPES_H */
*** a/src/include/utils/syscache.h
--- b/src/include/utils/syscache.h
***************
*** 70,75 **** enum SysCacheIdentifier
--- 70,77 ----
OPFAMILYOID,
PROCNAMEARGSNSP,
PROCOID,
+ RANGETYPE,
+ RANGESUBTYPE,
RELNAMENSP,
RELOID,
RULERELNAME,
*** a/src/include/utils/timestamp.h
--- b/src/include/utils/timestamp.h
***************
*** 264,269 **** extern Datum interval_part(PG_FUNCTION_ARGS);
--- 264,270 ----
extern Datum timestamp_zone(PG_FUNCTION_ARGS);
extern Datum timestamp_izone(PG_FUNCTION_ARGS);
extern Datum timestamp_timestamptz(PG_FUNCTION_ARGS);
+ extern Datum timestamp_float8(PG_FUNCTION_ARGS);
extern Datum timestamptz_in(PG_FUNCTION_ARGS);
extern Datum timestamptz_out(PG_FUNCTION_ARGS);
*** a/src/pl/plpgsql/src/pl_comp.c
--- b/src/pl/plpgsql/src/pl_comp.c
***************
*** 494,499 **** do_compile(FunctionCallInfo fcinfo,
--- 494,501 ----
{
if (rettypeid == ANYARRAYOID)
rettypeid = INT4ARRAYOID;
+ else if (rettypeid == ANYRANGEOID)
+ rettypeid = INT4RANGEOID;
else /* ANYELEMENT or ANYNONARRAY */
rettypeid = INT4OID;
/* XXX what could we use for ANYENUM? */
***************
*** 2123,2128 **** build_datatype(HeapTuple typeTup, int32 typmod, Oid collation)
--- 2125,2131 ----
case TYPTYPE_BASE:
case TYPTYPE_DOMAIN:
case TYPTYPE_ENUM:
+ case TYPTYPE_RANGE:
typ->ttype = PLPGSQL_TTYPE_SCALAR;
break;
case TYPTYPE_COMPOSITE:
***************
*** 2377,2384 **** 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.
*/
static void
plpgsql_resolve_polymorphic_argtypes(int numargs,
--- 2380,2387 ----
/*
* This is the same as the standard resolve_polymorphic_argtypes() function,
* but with a special case for validation: assume that polymorphic arguments
! * 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,
***************
*** 2411,2416 **** plpgsql_resolve_polymorphic_argtypes(int numargs,
--- 2414,2422 ----
case ANYENUMOID: /* XXX dubious */
argtypes[i] = INT4OID;
break;
+ case ANYRANGEOID:
+ argtypes[i] = INT4RANGEOID;
+ break;
case ANYARRAYOID:
argtypes[i] = INT4ARRAYOID;
break;
*** a/src/test/regress/expected/opr_sanity.out
--- b/src/test/regress/expected/opr_sanity.out
***************
*** 1015,1033 **** ORDER BY 1, 2, 3;
--- 1015,1044 ----
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,1050 **** ORDER BY 1, 2, 3;
2742 | 2 | @@@
2742 | 3 | <@
2742 | 4 | =
! (40 rows)
-- Check that all opclass search operators have selectivity estimators.
-- This is not absolutely required, but it seems a reasonable thing
--- 1055,1061 ----
2742 | 2 | @@@
2742 | 3 | <@
2742 | 4 | =
! (51 rows)
-- Check that all opclass search operators have selectivity estimators.
-- This is not absolutely required, but it seems a reasonable thing
*** /dev/null
--- b/src/test/regress/expected/rangetypes.out
***************
*** 0 ****
--- 1,746 ----
+ CREATE TABLE numrange_test (nr NUMRANGE);
+ create index numrange_test_btree on numrange_test(nr);
+ SET enable_seqscan = f;
+ -- negative test; should fail
+ INSERT INTO numrange_test VALUES('-[1.1, 2.2)');
+ ERROR: syntax error on range input "-[1.1, 2.2)", character 1
+ LINE 1: INSERT INTO numrange_test VALUES('-[1.1, 2.2)');
+ ^
+ INSERT INTO numrange_test VALUES('[1.1, NULL)');
+ ERROR: NULL range boundaries are not supported
+ LINE 1: INSERT INTO numrange_test VALUES('[1.1, NULL)');
+ ^
+ INSERT INTO numrange_test VALUES('[NULL, 2.2)');
+ ERROR: NULL range boundaries are not supported
+ LINE 1: INSERT INTO numrange_test VALUES('[NULL, 2.2)');
+ ^
+ INSERT INTO numrange_test VALUES('[NULL, NULL)');
+ ERROR: NULL range boundaries are not supported
+ LINE 1: INSERT INTO numrange_test VALUES('[NULL, NULL)');
+ ^
+ -- should succeed
+ INSERT INTO numrange_test VALUES('[-INF, INF)');
+ INSERT INTO numrange_test VALUES('[3, INF]');
+ INSERT INTO numrange_test VALUES('[-INF, 5)');
+ INSERT INTO numrange_test VALUES(range(1.1, 2.2));
+ INSERT INTO numrange_test VALUES('-');
+ INSERT INTO numrange_test VALUES(range(1.7));
+ SELECT empty(nr) FROM numrange_test;
+ empty
+ -------
+ f
+ f
+ f
+ f
+ t
+ f
+ (6 rows)
+
+ SELECT lower_inc(nr), lower(nr), upper(nr), upper_inc(nr) FROM numrange_test
+ WHERE NOT empty(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, range(1.9,1.91));
+ nr
+ ---------------
+ ( -INF, INF )
+ ( -INF, 5 )
+ [ 1.1, 2.2 )
+ (3 rows)
+
+ SELECT * FROM numrange_test WHERE nr @> range(1.0,10000.1);
+ nr
+ ---------------
+ ( -INF, INF )
+ (1 row)
+
+ SELECT * FROM numrange_test WHERE contained_by(range(-1e7,-10000.1), nr);
+ nr
+ ---------------
+ ( -INF, INF )
+ ( -INF, 5 )
+ (2 rows)
+
+ SELECT * FROM numrange_test WHERE 1.9 <@ nr;
+ nr
+ ---------------
+ ( -INF, INF )
+ ( -INF, 5 )
+ [ 1.1, 2.2 )
+ (3 rows)
+
+ SELECT * FROM numrange_test WHERE nr = '-';
+ nr
+ ----
+ -
+ (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 range(2.0, 1.0);
+ ERROR: range lower bound must be less than or equal to range upper bound
+ select range(2.0, 3.0) -|- range(3.0, 4.0);
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ select adjacent(range(2.0, 3.0), range(3.1, 4.0));
+ adjacent
+ ----------
+ f
+ (1 row)
+
+ select rangeii(2.0, 3.0) -|- range__(3.0, 4.0);
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ select range(1.0, 2.0) -|- rangeii(2.0, 3.0);
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ select adjacent(range_i(2.0, 3.0), range_i(1.0, 2.0));
+ adjacent
+ ----------
+ t
+ (1 row)
+
+ select range(1.1, 3.3) <@ range(0.1,10.1);
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ select range(0.1, 10.1) <@ range(1.1,3.3);
+ ?column?
+ ----------
+ f
+ (1 row)
+
+ select range(1.1, 2.2) - range(2.0, 3.0);
+ ?column?
+ --------------
+ [ 1.1, 2.0 )
+ (1 row)
+
+ select range(1.1, 2.2) - range(2.2, 3.0);
+ ?column?
+ --------------
+ [ 1.1, 2.2 )
+ (1 row)
+
+ select rangeii(1.1, 2.2) - range(2.0, 3.0);
+ ?column?
+ --------------
+ [ 1.1, 2.0 )
+ (1 row)
+
+ select minus(rangeii(10.1, 12.2), range_i(110.0,120.2));
+ minus
+ ----------------
+ [ 10.1, 12.2 ]
+ (1 row)
+
+ select minus(rangeii(10.1, 12.2), range_i(0.0,120.2));
+ minus
+ -------
+ -
+ (1 row)
+
+ select rangeii(4.5, 5.5) && range(5.5, 6.5);
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ select range(1.0, 2.0) << range(3.0, 4.0);
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ select range(1.0, 2.0) >> range(3.0, 4.0);
+ ?column?
+ ----------
+ f
+ (1 row)
+
+ select range(3.0, 70.0) &< range(6.6, 100.0);
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ select range(1.1, 2.2) < range(1.0, 200.2);
+ ?column?
+ ----------
+ f
+ (1 row)
+
+ select range(1.1, 2.2) < range__(1.1, 1.2);
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ select range(1.0, 2.0) + range(2.0, 3.0);
+ ?column?
+ --------------
+ [ 1.0, 3.0 )
+ (1 row)
+
+ select range(1.0, 2.0) + range(1.5, 3.0);
+ ?column?
+ --------------
+ [ 1.0, 3.0 )
+ (1 row)
+
+ select range(1.0, 2.0) + range(2.5, 3.0);
+ ERROR: range union resulted in two ranges
+ select range(1.0, 2.0) * range(2.0, 3.0);
+ ?column?
+ ----------
+ -
+ (1 row)
+
+ select range(1.0, 2.0) * range(1.5, 3.0);
+ ?column?
+ --------------
+ [ 1.5, 2.0 )
+ (1 row)
+
+ select range(1.0, 2.0) * range(2.5, 3.0);
+ ?column?
+ ----------
+ -
+ (1 row)
+
+ select * from numrange_test where nr < rangeii(-1000.0, -1000.0);
+ nr
+ ---------------
+ ( -INF, INF )
+ ( -INF, 5 )
+ -
+ (3 rows)
+
+ select * from numrange_test where nr < rangeii(0.0, 1.0);
+ nr
+ ---------------
+ ( -INF, INF )
+ ( -INF, 5 )
+ -
+ (3 rows)
+
+ select * from numrange_test where nr < rangeii(1000.0, 1001.0);
+ nr
+ ---------------
+ ( -INF, INF )
+ [ 3, INF )
+ ( -INF, 5 )
+ [ 1.1, 2.2 )
+ -
+ [ 1.7, 1.7 ]
+ (6 rows)
+
+ select * from numrange_test where nr > rangeii(-1001.0, -1000.0);
+ nr
+ --------------
+ [ 3, INF )
+ [ 1.1, 2.2 )
+ [ 1.7, 1.7 ]
+ (3 rows)
+
+ select * from numrange_test where nr > rangeii(0.0, 1.0);
+ nr
+ --------------
+ [ 3, INF )
+ [ 1.1, 2.2 )
+ [ 1.7, 1.7 ]
+ (3 rows)
+
+ select * from numrange_test where nr > rangeii(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('[-INF, 5)');
+ INSERT INTO numrange_test2 VALUES(range(1.1, 2.2));
+ INSERT INTO numrange_test2 VALUES(range(1.1, 2.2));
+ INSERT INTO numrange_test2 VALUES(range__(1.1, 2.2));
+ INSERT INTO numrange_test2 VALUES('-');
+ select * from numrange_test2 where nr = '-'::numrange;
+ nr
+ ----
+ -
+ (1 row)
+
+ select * from numrange_test2 where nr = range(1.1, 2.2);
+ nr
+ --------------
+ [ 1.1, 2.2 )
+ [ 1.1, 2.2 )
+ (2 rows)
+
+ select * from numrange_test2 where nr = range(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
+ --------------
+ -
+ ( -INF, 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
+ --------------
+ -
+ ( -INF, 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
+ --------------
+ -
+ ( -INF, 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 range__(1,10);
+ range__
+ -----------
+ [ 2, 10 )
+ (1 row)
+
+ select rangei_(1,10);
+ rangei_
+ -----------
+ [ 1, 10 )
+ (1 row)
+
+ select range_i(1,10);
+ range_i
+ -----------
+ [ 2, 11 )
+ (1 row)
+
+ select rangeii(1,10);
+ rangeii
+ -----------
+ [ 1, 11 )
+ (1 row)
+
+ -- test canonical form for daterange
+ select range__('2000-01-10'::date, '2000-01-20'::date);
+ range__
+ ----------------------------
+ [ 01-11-2000, 01-20-2000 )
+ (1 row)
+
+ select rangei_('2000-01-10'::date, '2000-01-20'::date);
+ rangei_
+ ----------------------------
+ [ 01-10-2000, 01-20-2000 )
+ (1 row)
+
+ select range_i('2000-01-10'::date, '2000-01-20'::date);
+ range_i
+ ----------------------------
+ [ 01-11-2000, 01-21-2000 )
+ (1 row)
+
+ select rangeii('2000-01-10'::date, '2000-01-20'::date);
+ rangeii
+ ----------------------------
+ [ 01-10-2000, 01-21-2000 )
+ (1 row)
+
+ -- test length()
+ select length(range(10.1,100.1));
+ length
+ --------
+ 90.0
+ (1 row)
+
+ select length('[2000-01-01 01:00:00, 2000-01-05 03:00:00)'::tsrange);
+ length
+ ------------------
+ @ 4 days 2 hours
+ (1 row)
+
+ select length('[2000-01-01 01:00:00, 2000-01-01 03:00:00)'::tstzrange);
+ length
+ -----------
+ @ 2 hours
+ (1 row)
+
+ select length('[2000-01-01, 2000-01-05)'::daterange);
+ length
+ --------
+ 4
+ (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 range(g, g+10) from generate_series(1,2000) g;
+ insert into test_range_gist select '-'::int4range from generate_series(1,500) g;
+ insert into test_range_gist select range(g, g+10000) from generate_series(1,1000) g;
+ insert into test_range_gist select '-'::int4range from generate_series(1,500) g;
+ insert into test_range_gist select range_linfi(g*10) from generate_series(1,100) g;
+ insert into test_range_gist select range_uinf_(g*10) from generate_series(1,100) g;
+ insert into test_range_gist select range(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 @> '-'::int4range;
+ count
+ -------
+ 6200
+ (1 row)
+
+ select count(*) from test_range_gist where ir = range(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 @> range(10,20);
+ count
+ -------
+ 111
+ (1 row)
+
+ select count(*) from test_range_gist where ir && range(10,20);
+ count
+ -------
+ 158
+ (1 row)
+
+ select count(*) from test_range_gist where ir <@ range(10,50);
+ count
+ -------
+ 1062
+ (1 row)
+
+ select count(*) from (select * from test_range_gist where ir?) s where ir << range(100,500);
+ count
+ -------
+ 189
+ (1 row)
+
+ select count(*) from (select * from test_range_gist where ir?) s where ir >> range(100,500);
+ count
+ -------
+ 3554
+ (1 row)
+
+ select count(*) from (select * from test_range_gist where ir?) s where ir &< range(100,500);
+ count
+ -------
+ 1029
+ (1 row)
+
+ select count(*) from (select * from test_range_gist where ir?) s where ir &> range(100,500);
+ count
+ -------
+ 4794
+ (1 row)
+
+ select count(*) from (select * from test_range_gist where ir?) s where ir -|- range(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 @> '-'::int4range;
+ count
+ -------
+ 6200
+ (1 row)
+
+ select count(*) from test_range_gist where ir = range(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 @> range(10,20);
+ count
+ -------
+ 111
+ (1 row)
+
+ select count(*) from test_range_gist where ir && range(10,20);
+ count
+ -------
+ 158
+ (1 row)
+
+ select count(*) from test_range_gist where ir <@ range(10,50);
+ count
+ -------
+ 1062
+ (1 row)
+
+ select count(*) from test_range_gist where ir << range(100,500);
+ count
+ -------
+ 189
+ (1 row)
+
+ select count(*) from test_range_gist where ir >> range(100,500);
+ count
+ -------
+ 3554
+ (1 row)
+
+ select count(*) from test_range_gist where ir &< range(100,500);
+ count
+ -------
+ 1029
+ (1 row)
+
+ select count(*) from test_range_gist where ir &> range(100,500);
+ count
+ -------
+ 4794
+ (1 row)
+
+ select count(*) from test_range_gist where ir -|- range(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 @> '-'::int4range;
+ count
+ -------
+ 6200
+ (1 row)
+
+ select count(*) from test_range_gist where ir = range(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 @> range(10,20);
+ count
+ -------
+ 111
+ (1 row)
+
+ select count(*) from test_range_gist where ir && range(10,20);
+ count
+ -------
+ 158
+ (1 row)
+
+ select count(*) from test_range_gist where ir <@ range(10,50);
+ count
+ -------
+ 1062
+ (1 row)
+
+ select count(*) from test_range_gist where ir << range(100,500);
+ count
+ -------
+ 189
+ (1 row)
+
+ select count(*) from test_range_gist where ir >> range(100,500);
+ count
+ -------
+ 3554
+ (1 row)
+
+ select count(*) from test_range_gist where ir &< range(100,500);
+ count
+ -------
+ 1029
+ (1 row)
+
+ select count(*) from test_range_gist where ir &> range(100,500);
+ count
+ -------
+ 4794
+ (1 row)
+
+ select count(*) from test_range_gist where ir -|- range(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(range(123), range(1), '[2010-01-02 10:00, 2010-01-02 11:00)');
+ insert into test_range_excl
+ values(range(123), range(2), '[2010-01-02 11:00, 2010-01-02 12:00)');
+ insert into test_range_excl
+ values(range(123), range(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(range(124), range(3), '[2010-01-02 10:10, 2010-01-02 11:10)');
+ insert into test_range_excl
+ values(range(125), range(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 range_i(10000000000::int8, 20000000000::int8);
+ range_i
+ ------------------------------
+ [ 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
+ --
+ create type float8range as range (subtype=float8);
+ 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(range((-100.00007)::float8, '1.111113e9'::float8));
+ select * from float8range_test;
+ f8r | i
+ ----------------------------+---
+ [ -100.00007, 1111113000 ) |
+ (1 row)
+
+ drop table float8range_test;
+ --
+ -- Test rangetypes over domains
+ --
+ create domain mydomain as int4;
+ create type mydomainrange as range(subtype=mydomain);
+ select '[4,50)'::mydomainrange @> 7::mydomain;
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ --
+ -- Test collations
+ --
+ create type textrange_c as range(subtype=text, collation="C");
+ select '[a, Z)'::textrange_c @> 'b'::text;
+ ERROR: range lower bound must be less than or equal to range upper bound
+ LINE 1: select '[a, Z)'::textrange_c @> 'b'::text;
+ ^
+ drop type textrange_c;
+ create type textrange_en_us as range(subtype=text, collation="en_US");
+ select '[a, Z)'::textrange_en_us @> 'b'::text;
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ drop type textrange_en_us;
*** a/src/test/regress/expected/sanity_check.out
--- b/src/test/regress/expected/sanity_check.out
***************
*** 116,121 **** SELECT relname, relhasindex
--- 116,122 ----
pg_opfamily | t
pg_pltemplate | t
pg_proc | t
+ pg_range | t
pg_rewrite | t
pg_seclabel | t
pg_shdepend | t
***************
*** 158,164 **** SELECT relname, relhasindex
timetz_tbl | f
tinterval_tbl | f
varchar_tbl | f
! (147 rows)
--
-- another sanity check: every system catalog that has OIDs should have
--- 159,165 ----
timetz_tbl | f
tinterval_tbl | f
varchar_tbl | f
! (148 rows)
--
-- another sanity check: every system catalog that has OIDs should have
*** a/src/test/regress/expected/type_sanity.out
--- b/src/test/regress/expected/type_sanity.out
***************
*** 17,23 **** 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
NOT p1.typisdefined OR
(p1.typalign not in ('c', 's', 'i', 'd')) OR
(p1.typstorage not in ('p', 'x', 'e', 'm'));
--- 17,23 ----
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', 'r')) OR
NOT p1.typisdefined OR
(p1.typalign not in ('c', 's', 'i', 'd')) OR
(p1.typstorage not in ('p', 'x', 'e', 'm'));
*** a/src/test/regress/parallel_schedule
--- b/src/test/regress/parallel_schedule
***************
*** 13,19 **** 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
# Depends on things setup during char, varchar and text
test: strings
--- 13,19 ----
# ----------
# The first group of parallel tests
# ----------
! 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
*** /dev/null
--- b/src/test/regress/sql/rangetypes.sql
***************
*** 0 ****
--- 1,257 ----
+
+ CREATE TABLE numrange_test (nr NUMRANGE);
+ create index numrange_test_btree on numrange_test(nr);
+ SET enable_seqscan = f;
+
+ -- negative test; should fail
+ INSERT INTO numrange_test VALUES('-[1.1, 2.2)');
+ INSERT INTO numrange_test VALUES('[1.1, NULL)');
+ INSERT INTO numrange_test VALUES('[NULL, 2.2)');
+ INSERT INTO numrange_test VALUES('[NULL, NULL)');
+
+ -- should succeed
+ INSERT INTO numrange_test VALUES('[-INF, INF)');
+ INSERT INTO numrange_test VALUES('[3, INF]');
+ INSERT INTO numrange_test VALUES('[-INF, 5)');
+ INSERT INTO numrange_test VALUES(range(1.1, 2.2));
+ INSERT INTO numrange_test VALUES('-');
+ INSERT INTO numrange_test VALUES(range(1.7));
+
+ SELECT empty(nr) FROM numrange_test;
+ SELECT lower_inc(nr), lower(nr), upper(nr), upper_inc(nr) FROM numrange_test
+ WHERE NOT empty(nr) AND NOT lower_inf(nr) AND NOT upper_inf(nr);
+
+ SELECT * FROM numrange_test WHERE contains(nr, range(1.9,1.91));
+ SELECT * FROM numrange_test WHERE nr @> range(1.0,10000.1);
+ SELECT * FROM numrange_test WHERE contained_by(range(-1e7,-10000.1), nr);
+ SELECT * FROM numrange_test WHERE 1.9 <@ nr;
+ SELECT * FROM numrange_test WHERE nr = '-';
+ SELECT * FROM numrange_test WHERE range_eq(nr, '(1.1, 2.2)');
+ SELECT * FROM numrange_test WHERE nr = '[1.1, 2.2)';
+
+ select range(2.0, 1.0);
+
+ select range(2.0, 3.0) -|- range(3.0, 4.0);
+ select adjacent(range(2.0, 3.0), range(3.1, 4.0));
+ select rangeii(2.0, 3.0) -|- range__(3.0, 4.0);
+ select range(1.0, 2.0) -|- rangeii(2.0, 3.0);
+ select adjacent(range_i(2.0, 3.0), range_i(1.0, 2.0));
+
+ select range(1.1, 3.3) <@ range(0.1,10.1);
+ select range(0.1, 10.1) <@ range(1.1,3.3);
+
+ select range(1.1, 2.2) - range(2.0, 3.0);
+ select range(1.1, 2.2) - range(2.2, 3.0);
+ select rangeii(1.1, 2.2) - range(2.0, 3.0);
+ select minus(rangeii(10.1, 12.2), range_i(110.0,120.2));
+ select minus(rangeii(10.1, 12.2), range_i(0.0,120.2));
+
+ select rangeii(4.5, 5.5) && range(5.5, 6.5);
+ select range(1.0, 2.0) << range(3.0, 4.0);
+ select range(1.0, 2.0) >> range(3.0, 4.0);
+ select range(3.0, 70.0) &< range(6.6, 100.0);
+
+ select range(1.1, 2.2) < range(1.0, 200.2);
+ select range(1.1, 2.2) < range__(1.1, 1.2);
+
+ select range(1.0, 2.0) + range(2.0, 3.0);
+ select range(1.0, 2.0) + range(1.5, 3.0);
+ select range(1.0, 2.0) + range(2.5, 3.0);
+
+ select range(1.0, 2.0) * range(2.0, 3.0);
+ select range(1.0, 2.0) * range(1.5, 3.0);
+ select range(1.0, 2.0) * range(2.5, 3.0);
+
+ select * from numrange_test where nr < rangeii(-1000.0, -1000.0);
+ select * from numrange_test where nr < rangeii(0.0, 1.0);
+ select * from numrange_test where nr < rangeii(1000.0, 1001.0);
+ select * from numrange_test where nr > rangeii(-1001.0, -1000.0);
+ select * from numrange_test where nr > rangeii(0.0, 1.0);
+ select * from numrange_test where nr > rangeii(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('[-INF, 5)');
+ INSERT INTO numrange_test2 VALUES(range(1.1, 2.2));
+ INSERT INTO numrange_test2 VALUES(range(1.1, 2.2));
+ INSERT INTO numrange_test2 VALUES(range__(1.1, 2.2));
+ INSERT INTO numrange_test2 VALUES('-');
+
+ select * from numrange_test2 where nr = '-'::numrange;
+ select * from numrange_test2 where nr = range(1.1, 2.2);
+ select * from numrange_test2 where nr = range(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 range__(1,10);
+ select rangei_(1,10);
+ select range_i(1,10);
+ select rangeii(1,10);
+
+ -- test canonical form for daterange
+ select range__('2000-01-10'::date, '2000-01-20'::date);
+ select rangei_('2000-01-10'::date, '2000-01-20'::date);
+ select range_i('2000-01-10'::date, '2000-01-20'::date);
+ select rangeii('2000-01-10'::date, '2000-01-20'::date);
+
+ -- test length()
+ select length(range(10.1,100.1));
+ select length('[2000-01-01 01:00:00, 2000-01-05 03:00:00)'::tsrange);
+ select length('[2000-01-01 01:00:00, 2000-01-01 03:00:00)'::tstzrange);
+ select length('[2000-01-01, 2000-01-05)'::daterange);
+
+ 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 range(g, g+10) from generate_series(1,2000) g;
+ insert into test_range_gist select '-'::int4range from generate_series(1,500) g;
+ insert into test_range_gist select range(g, g+10000) from generate_series(1,1000) g;
+ insert into test_range_gist select '-'::int4range from generate_series(1,500) g;
+ insert into test_range_gist select range_linfi(g*10) from generate_series(1,100) g;
+ insert into test_range_gist select range_uinf_(g*10) from generate_series(1,100) g;
+ insert into test_range_gist select range(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 @> '-'::int4range;
+ select count(*) from test_range_gist where ir = range(10,20);
+ select count(*) from test_range_gist where ir @> 10;
+ select count(*) from test_range_gist where ir @> range(10,20);
+ select count(*) from test_range_gist where ir && range(10,20);
+ select count(*) from test_range_gist where ir <@ range(10,50);
+ select count(*) from (select * from test_range_gist where ir?) s where ir << range(100,500);
+ select count(*) from (select * from test_range_gist where ir?) s where ir >> range(100,500);
+ select count(*) from (select * from test_range_gist where ir?) s where ir &< range(100,500);
+ select count(*) from (select * from test_range_gist where ir?) s where ir &> range(100,500);
+ select count(*) from (select * from test_range_gist where ir?) s where ir -|- range(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 @> '-'::int4range;
+ select count(*) from test_range_gist where ir = range(10,20);
+ select count(*) from test_range_gist where ir @> 10;
+ select count(*) from test_range_gist where ir @> range(10,20);
+ select count(*) from test_range_gist where ir && range(10,20);
+ select count(*) from test_range_gist where ir <@ range(10,50);
+ select count(*) from test_range_gist where ir << range(100,500);
+ select count(*) from test_range_gist where ir >> range(100,500);
+ select count(*) from test_range_gist where ir &< range(100,500);
+ select count(*) from test_range_gist where ir &> range(100,500);
+ select count(*) from test_range_gist where ir -|- range(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 @> '-'::int4range;
+ select count(*) from test_range_gist where ir = range(10,20);
+ select count(*) from test_range_gist where ir @> 10;
+ select count(*) from test_range_gist where ir @> range(10,20);
+ select count(*) from test_range_gist where ir && range(10,20);
+ select count(*) from test_range_gist where ir <@ range(10,50);
+ select count(*) from test_range_gist where ir << range(100,500);
+ select count(*) from test_range_gist where ir >> range(100,500);
+ select count(*) from test_range_gist where ir &< range(100,500);
+ select count(*) from test_range_gist where ir &> range(100,500);
+ select count(*) from test_range_gist where ir -|- range(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(range(123), range(1), '[2010-01-02 10:00, 2010-01-02 11:00)');
+ insert into test_range_excl
+ values(range(123), range(2), '[2010-01-02 11:00, 2010-01-02 12:00)');
+ insert into test_range_excl
+ values(range(123), range(3), '[2010-01-02 10:10, 2010-01-02 11:10)');
+ insert into test_range_excl
+ values(range(124), range(3), '[2010-01-02 10:10, 2010-01-02 11:10)');
+ insert into test_range_excl
+ values(range(125), range(1), '[2010-01-02 10:10, 2010-01-02 11:10)');
+
+ drop table test_range_excl;
+
+ -- test bigint ranges
+ select range_i(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
+ --
+
+ create type float8range as range (subtype=float8);
+ select '[123.001, 5.e9)'::float8range @> 888.882::float8;
+ create table float8range_test(f8r float8range, i int);
+ insert into float8range_test values(range((-100.00007)::float8, '1.111113e9'::float8));
+ select * from float8range_test;
+ drop table float8range_test;
+
+ --
+ -- Test rangetypes over domains
+ --
+
+ create domain mydomain as int4;
+ create type mydomainrange as range(subtype=mydomain);
+ select '[4,50)'::mydomainrange @> 7::mydomain;
+
+ --
+ -- Test collations
+ --
+
+ create type textrange_c as range(subtype=text, collation="C");
+ select '[a, Z)'::textrange_c @> 'b'::text;
+ drop type textrange_c;
+
+ create type textrange_en_us as range(subtype=text, collation="en_US");
+ select '[a, Z)'::textrange_en_us @> 'b'::text;
+ drop type textrange_en_us;
*** a/src/test/regress/sql/type_sanity.sql
--- b/src/test/regress/sql/type_sanity.sql
***************
*** 20,26 **** 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
NOT p1.typisdefined OR
(p1.typalign not in ('c', 's', 'i', 'd')) OR
(p1.typstorage not in ('p', 'x', 'e', 'm'));
--- 20,26 ----
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', 'r')) OR
NOT p1.typisdefined OR
(p1.typalign not in ('c', 's', 'i', 'd')) OR
(p1.typstorage not in ('p', 'x', 'e', 'm'));