*** a/doc/src/sgml/catalogs.sgml
--- b/doc/src/sgml/catalogs.sgml
***************
*** 219,224 ****
--- 219,229 ----
+ pg_range
+ information about range types
+
+
+ pg_rewritequery rewrite rules
***************
*** 4594,4599 ****
--- 4599,4683 ----
+
+ pg_range
+
+
+ pg_range
+
+
+
+ The catalog pg_range stores information about range types.
+
+
+
+ pg_range> Columns
+
+
+
+
+ Name
+ Type
+ References
+ Description
+
+
+
+
+
+ rngtypid
+ oid
+ pg_type.oid
+ The type that is a range type
+
+
+
+ rngsubtype
+ oid
+ pg_type.oid
+ The subtype of the range type, e.g. integer is the subtype of int4range
+
+
+
+ rngcollation
+ oid
+ pg_collation.oid
+ The collation used when comparing range boundaries
+
+
+
+ rngsubopc
+ oid
+ pg_opclass.oid
+ The operator class used when comparing range boundaries
+
+
+
+ rngcanonical
+ regproc
+ pg_proc.oid
+ The function that changes a range into its canonical form
+
+
+
+ rngsubfloat
+ regproc
+ pg_proc.oid
+ The function for converting the subtype to a double precision, used for GiST support
+
+
+
+
+
+
+
+ pg_class.relhasrules
+ must be true if a table has any rules in this catalog.
+
+
+
+
+
pg_rewrite
*** a/doc/src/sgml/datatype.sgml
--- b/doc/src/sgml/datatype.sgml
***************
*** 4173,4178 **** SET xmloption TO { DOCUMENT | CONTENT };
--- 4173,4180 ----
&rowtypes;
+ &rangetypes;
+
Object Identifier Types
***************
*** 4444,4449 **** SELECT * FROM pg_attribute
--- 4446,4455 ----
+ anyrange
+
+
+ void
***************
*** 4520,4525 **** SELECT * FROM pg_attribute
--- 4526,4538 ----
+ anyrange>
+ Indicates that a function accepts any range data type
+ (see and
+ ).
+
+
+ anynonarray>Indicates that a function accepts any non-array data type
(see ).
***************
*** 4583,4589 **** 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>.
--- 4596,4603 ----
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
***************
*** 10457,10462 **** SELECT NULLIF(value, '(none)') ...
--- 10457,10766 ----
+
+ Range Functions and Operators
+
+
+ shows the operators
+ available for range types.
+
+
+
+ Range Operators
+
+
+
+ Operator
+ Description
+ Example
+ Result
+
+
+
+
+ =
+ equal
+ int4range(1,5) = '[1,4]'::int4range
+ t
+
+
+
+ <>
+ not equal
+ numrange(1.1,2.2) <> numrange(1.1,2.3)
+ t
+
+
+
+ <
+ less than
+ int4range(1,10) < int4range(2,3)
+ t
+
+
+
+ >
+ greater than
+ int4range(1,10) > int4range(1,5)
+ t
+
+
+
+ <=
+ less than or equal
+ numrange(1.1,2.2) <= numrange(1.1,2.2)
+ t
+
+
+
+ >=
+ greater than or equal
+ numrange(1.1,2.2) >= numrange(1.1,2.0)
+ t
+
+
+
+ @>
+ contains
+ '[2011-01-01,2011-03-01)'::tsrange @> '2011-01-10'::timestamp
+ t
+
+
+
+ <@
+ is contained by
+ int4range(2,4) <@ int4range(1,7)
+ t
+
+
+
+ &&
+ overlap (have points in common)
+ int8range(3,7) && int8range(4,12)
+ t
+
+
+
+ <<
+ strictly left of
+ int8range(1,10) << int8range(100,110)
+ t
+
+
+
+ >>
+ strictly right of
+ int8range(50,60) >> int8range(20,30)
+ t
+
+
+
+ &<
+ Does not extend to the right of?
+ int8range(1,20) &< int8range(18,20)
+ t
+
+
+
+ &>
+ Does not extend to the left of?
+ int8range(7,20) &> int8range(5,10)
+ t
+
+
+
+ -|-
+ adjacent?
+ numrange(1.1,2.2) -|- numrange(2.2,3.3)
+ t
+
+
+
+ +
+ Union
+ numrange(5,15) + numrange(10,20)
+ [5,20)
+
+
+
+ -
+ Difference
+ int8range(5,15) - int8range(10,20)
+ [5,10)
+
+
+
+ *
+ Intersection
+ int8range(5,15) * int8range(10,20)
+ [10,15)
+
+
+
+ !?
+ Is empty?
+ 'empty'::int4range !?
+ t
+
+
+
+ ?
+ Is non-empty?
+ numrange(1.0,2.0)?
+ t
+
+
+
+
+
+
+ Range comparisons compare the lower bounds first, and only if
+ equal, compare the upper bounds. This is generally most useful for
+ B-tree indexes, rather than being useful comparisons by themselves.
+
+
+
+ See for more details about range operator
+ behavior.
+
+
+
+ shows the functions
+ available for use with range types. See
+ for more information and examples of the use of these functions.
+
+
+
+ lower
+
+
+ upper
+
+
+ empty
+
+
+ non_empty
+
+
+ lower_inc
+
+
+ upper_inc
+
+
+ lower_inf
+
+
+ upper_inf
+
+
+
+ Range Functions
+
+
+
+ Function
+ Return Type
+ Description
+ Example
+ Result
+
+
+
+
+
+
+ lower(anyrange)
+
+
+ anyrange
+ lower bound of range
+ lower(numrange(1.1,2.2))
+ 1.1
+
+
+
+
+ upper(anyrange)
+
+
+ anyrange
+ upper bound of range
+ upper(numrange(1.1,2.2))
+ 2.2
+
+
+
+
+ empty(anyrange)
+
+
+ anyrange
+ is the range empty?
+ empty(numrange(1.1,2.2))
+ false
+
+
+
+
+ non_empty(anyrange)
+
+
+ anyrange
+ is the range non-empty?
+ non_empty(numrange(1.1,2.2))
+ true
+
+
+
+
+ lower_inc(anyrange)
+
+
+ anyrange
+ is the lower bound of the range inclusive?
+ lower_inc(numrange(1.1,2.2))
+ true
+
+
+
+
+ upper_inc(anyrange)
+
+
+ anyrange
+ is the upper bound of the range inclusive?
+ upper_inc(numrange(1.1,2.2))
+ false
+
+
+
+
+ lower_inf(anyrange)
+
+
+ anyrange
+ is the lower bound of the range infinite?
+ lower_inf('(,)'::daterange)
+ true
+
+
+
+
+ upper_inf(anyrange)
+
+
+ anyrange
+ is the upper bound of the range infinite?
+ upper_inf('(,)'::daterange)
+ true
+
+
+
+
+
+
Aggregate Functions
*** 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,373 ----
+
+
+
+ Range Types
+
+
+ range type
+
+
+
+ Range types are data types representing a range of values over some
+ sub-type with a total order. For instance, ranges
+ of timestamp might be used to represent the ranges of
+ time that a meeting room is reserved. In this case the data type
+ is tsrange (short for "timestamp range"),
+ and timestamp is the sub-type with a total order.
+
+
+
+ Range types are useful because they represent many points in a
+ single value. The use of time and date ranges for scheduling
+ purposes is the clearest example; but price ranges, measurement
+ ranges from an instrument, etc., are also useful.
+
+
+
+ Built-in Range Types
+
+ PostgreSQL comes with the following built-in range types:
+
+
+
+ INT4RANGE -- Range of INTEGER. This is a discrete range type, see .
+
+
+
+
+ INT8RANGE -- Range of BIGINT. This is a discrete range type, see .
+
+
+
+
+ NUMRANGE -- Range of NUMERIC.
+
+
+
+
+ TSRANGE -- Range of TIMESTAMP WITHOUT TIME ZONE.
+
+
+
+
+ TSTZRANGE -- Range of TIMESTAMP WITH TIME ZONE.
+
+
+
+
+ DATERANGE -- Range of DATE. This is a discrete range type, see .
+
+
+
+ In addition, you can define your own; see for more information.
+
+
+
+
+ Examples
+
+
+ CREATE TABLE reservation ( during TSRANGE );
+ INSERT INTO reservation VALUES
+ ( '[2010-01-01 14:30, 2010-01-01 15:30)' );
+
+ -- Containment
+ SELECT int4range(10, 20) @> 3;
+
+ -- Overlaps
+ SELECT numrange(11.1, 22.2) && numrange(20.0, 30.0);
+
+ -- Find the upper bound:
+ SELECT upper(int8range(15, 25));
+
+ -- Compute the intersection:
+ SELECT int4range(10, 20) * int4range(15, 25);
+
+ -- Is the range non-empty?
+ SELECT numrange(1, 5)? ;
+
+
+
+ See
+ and for complete lists of
+ functions and operators on range types.
+
+
+
+
+ Inclusive and Exclusive Bounds
+
+ Every range has two bounds, the lower bound and the upper bound. All
+ points in between those values are included in the range. An
+ inclusive bound means that the boundary point itself is included in
+ the range as well, while an exclusive bound means that the boundary
+ point is not included in the range.
+
+
+ An inclusive lower bound is represented by [
+ while an exclusive lower bound is represented
+ by ( (see
+ and below). Likewise, an inclusive
+ upper bound is represented by ], while an
+ exclusive upper bound is represented by ).
+
+
+ Functions lower_inc
+ and upper_inc test the inclusivity of the lower
+ and upper bounds of a range, respectively.
+
+
+
+
+ Infinite (unbounded) Ranges
+
+ The lower bound of a range can be omitted, meaning that all points
+ less (or equal to, if inclusive) than the upper bound are included
+ in the range. Likewise, if the upper bound of the range is omitted,
+ then all points greater than (or equal to, if omitted) the lower
+ bound are included in the range. If both lower and upper bounds are
+ omitted, all points are considered to be in the range.
+
+
+ Functions lower_inf
+ and upper_inf test the range for infinite lower
+ and upper bounds of a range, respectively.
+
+
+
+
+ Input/Output
+
+ The input follows one of the following patterns:
+
+ (lower-bound,upper-bound)
+ (lower-bound,upper-bound]
+ [lower-bound,upper-bound)
+ [lower-bound,upper-bound]
+ empty
+
+ Notice that the final pattern is empty, which
+ represents an empty range (a range that contains no points).
+
+
+ The lower-bound may be either a string
+ that is valid input for the sub-type, or omitted (to indicate no
+ lower bound); and upper-bound may be
+ either a string that is valid input for the sub-type, or omitted (to
+ indicate no upper bound).
+
+
+ Either the lower-bound or
+ the upper-bound may be quoted
+ using "" (double quotation marks), which will allow
+ special characters such as ",". Within quotation
+ marks, "\" (backslash) serves as an escape
+ character.
+
+
+ The choice between the other input formats affects the inclusivity
+ of the bounds. See .
+
+
+ Examples:
+
+ -- includes point 3, does not include point 7, and does include all points in between
+ select '[3,7)'
+
+ -- does not include either 3 or 7, but includes all points in between
+ select '(3,7)'
+
+ -- includes only the single point 4
+ select '[4,4]'
+
+
+
+
+
+ Constructing Ranges
+
+ Each range type has a constructor by the same name. The constructor
+ accepts from zero to three arguments. The zero-argument form
+ constructs an empty range; the one-argument form constructs a
+ singleton range; the two-argument form constructs a range
+ in [ ) form; and the three-argument form
+ constructs a range in a form specified by the third argument. For
+ example:
+
+ -- Three-argument form: lower bound, upper bound, and third argument indicating
+ -- inclusivity/exclusivity of bounds (if omitted, defaults to '[)').
+ SELECT numrange(1.0, 14.0, '(]');
+
+ -- The int4range input will exclude the lower bound and include the upper bound; but the
+ -- resulting output will appear in the canonical form; see .
+ SELECT int8range(1, 14, '(]');
+
+ -- Single argument form constructs a singleton range; that is a range consisting of just
+ -- one point.
+ SELECT numrange(11.1);
+
+ -- Zero-argument form constructs and empty range.
+ SELECT numrange();
+
+ -- Using NULL for a bound causes the range to be unbounded on that side; that is, negative
+ -- infinity for the lower bound or positive infinity for the upper bound.
+ SELECT numrange(NULL,2.2);
+
+
+
+
+
+ Discrete Range Types
+
+ Discrete ranges are those that have a
+ defined canonical function. Loosely speaking, a
+ discrete range has a sub-type with a well-defined "step";
+ e.g. INTEGER or DATE.
+
+
+ The canonical function should take an input range
+ value, and return an equal range value that may have a different
+ formatting. For instance, the integer range [1,
+ 7] could be represented by the equal integer
+ range [1, 8). The two values are equal because
+ there are no points within the integer domain
+ between 7 and 8, so not
+ including the end point 8 is the same as
+ including the end point 7. The canonical output
+ for two values that are equal, like [1, 7]
+ and [1, 8), must be equal. It doesn't matter
+ which representation you choose to be the canonical one, as long as
+ two equal values with different formattings are always mapped to the
+ same value with the same formatting. If the canonical function is
+ not specified, then ranges with different formatting
+ (e.g. [1, 7] and [1, 8)) will
+ always be treated as unequal.
+
+
+ For types such as NUMRANGE, this is not possible,
+ because there are always points in between two
+ distinct NUMERIC values.
+
+
+ The built-in range
+ types INT4RANGE, INT8RANGE,
+ and DATERNAGE all use a canonical form that includes
+ the lower bound and excludes the upper bound; that is, [
+ ). User-defined ranges can use other conventions, however.
+
+
+
+
+ Defining New Range Types
+
+ Users can define their own range types. The most common reason to do
+ this is to use ranges where the subtype is not among the built-in
+ range types, e.g. a range of type FLOAT (or, if the
+ subtype itself is a user-defined type).
+
+
+ For example: to define a new range type of sub-type DOUBLE PRECISION:
+
+ CREATE TYPE FLOATRANGE AS RANGE (
+ SUBTYPE = DOUBLE PRECISION
+ );
+
+ SELECT '[1.234, 5.678]'::floatrange;
+
+ Because DOUBLE PRECISION has no meaningful "step", we
+ do not define a canonical
+ function. See for more
+ information.
+
+
+ Defining your own range type also allows you to specify a different
+ operator class or collation to use (which affects the points that
+ fall between the range boundaries), or a different canonicalization
+ function.
+
+
+
+
+
+ range type
+ gist
+
+ Indexing
+
+ GiST indexes can be applied to a table containing a range type. For instance:
+
+ CREATE INDEX reservation_idx ON reservation USING gist (during);
+
+ This index may speed up queries
+ involving &&
+ (overlaps), @> (contains), and all the boolean
+ operators found in this
+ table: .
+
+
+
+
+
+ range type
+ exclude
+
+ Constraints on Ranges
+
+ While UNIQUE is a natural constraint for scalar
+ values, it is usually unsuitable for range types. Instead, an
+ exclusion constraint is often more appropriate
+ (see CREATE TABLE
+ ... CONSTRAINT ... EXCLUDE). Exclusion constraints allow the
+ specification of constraints such as "non-overlapping" on a range
+ type. For example:
+
+ ALTER TABLE reservation
+ ADD EXCLUDE USING gist (during WITH &&);
+
+ That constraint will prevent any overlapping values from existing
+ in the table at the same time:
+
+ INSERT INTO reservation VALUES
+ ( '[2010-01-01 11:30, 2010-01-01 13:00)' );
+ -- Result: INSERT 0 1
+ INSERT INTO reservation VALUES
+ ( '[2010-01-01 14:45, 2010-01-01 15:45)' );
+ -- Result:
+ -- ERROR: conflicting key value violates exclusion constraint "reservation_during_excl"
+ -- DETAIL: Key (during)=([ 2010-01-01 14:45:00, 2010-01-01 15:45:00 )) conflicts with
+ -- existing key (during)=([ 2010-01-01 14:30:00, 2010-01-01 15:30:00 )).
+
+
+
+ Combine range types and exclusion constraints
+ with btree_gist for maximum
+ flexibility defining
+ constraints. After btree_gist is installed, the
+ following constraint will prevent overlapping ranges only if the
+ meeting room numbers are equal:
+
+
+ CREATE TABLE room_reservation
+ (
+ room TEXT,
+ during TSRANGE,
+ EXCLUDE USING gist (room WITH =, during WITH &&)
+ );
+
+ INSERT INTO room_reservation VALUES
+ ( '123A', '[2010-01-01 14:00, 2010-01-01 15:00)' );
+ -- Result: INSERT 0 1
+ INSERT INTO room_reservation VALUES
+ ( '123A', '[2010-01-01 14:30, 2010-01-01 15:30)' );
+ -- Result:
+ -- ERROR: conflicting key value violates exclusion constraint "room_reservation_room_during_excl"
+ -- DETAIL: Key (room, during)=(123A, [ 2010-01-01 14:30:00, 2010-01-01 15:30:00 )) conflicts with
+ -- existing key (room, during)=(123A, [ 2010-01-01 14:00:00, 2010-01-01 15:00:00 )).
+ INSERT INTO room_reservation VALUES
+ ( '123B', '[2010-01-01 14:30, 2010-01-01 15:30)' );
+ -- Result: INSERT 0 1
+
+
+
+
+
*** a/doc/src/sgml/ref/create_type.sgml
--- b/doc/src/sgml/ref/create_type.sgml
***************
*** 27,32 **** CREATE TYPE name AS
--- 27,41 ----
CREATE TYPE name AS ENUM
( [ 'label' [, ... ] ] )
+ CREATE TYPE name AS RANGE (
+ SUBTYPE = subtype,
+ [ , SUBTYPE_OPCLASS = subtype_operator_class ]
+ [ , SUBTYPE_FLOAT = subtype_float_function ]
+ [ , CANONICAL = canonical_function ]
+ [ , ANALYZE = analyze_function ]
+ [ , COLLATION = collation ]
+ )
+
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.)
--- 107,167 ----
+
+ 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 with an associated btree opclass (uses the type's
+ default btree operator class unless specified with
+ subtype_operator_class).
+
+
+
+ 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.
+
+
+
+ The collation option
+ specifies the collation used when determining the total order for
+ the range.
+
+
+
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
***************
*** 91,98 **** ProcedureCreate(const char *procedureName,
--- 91,101 ----
int parameterCount;
int allParamCount;
Oid *allParams;
+ char *modes = NULL;
bool genericInParam = false;
bool genericOutParam = false;
+ bool anyrangeInParam = false;
+ bool anyrangeOutParam = false;
bool internalInParam = false;
bool internalOutParam = false;
Oid variadicType = InvalidOid;
***************
*** 152,157 **** ProcedureCreate(const char *procedureName,
--- 155,178 ----
allParams = parameterTypes->values;
}
+ if (parameterModes != PointerGetDatum(NULL))
+ {
+ /*
+ * We expect the array to be a 1-D CHAR array; verify that. We don't
+ * need to use deconstruct_array() since the array data is just going
+ * to look like a C array of char values.
+ */
+ ArrayType *modesArray = (ArrayType *) DatumGetPointer(parameterModes);
+
+ if (ARR_NDIM(modesArray) != 1 ||
+ ARR_DIMS(modesArray)[0] != allParamCount ||
+ ARR_HASNULL(modesArray) ||
+ ARR_ELEMTYPE(modesArray) != CHAROID)
+ elog(ERROR, "parameterModes is not a 1-D char array");
+ modes = (char *) ARR_DATA_PTR(modesArray);
+ }
+
+
/*
* Do not allow polymorphic return type unless at least one input argument
* is polymorphic. Also, do not allow return type INTERNAL unless at
***************
*** 161,166 **** ProcedureCreate(const char *procedureName,
--- 182,190 ----
{
switch (parameterTypes->values[i])
{
+ case ANYRANGEOID:
+ anyrangeInParam = true;
+ /* FALL THROUGH */
case ANYARRAYOID:
case ANYELEMENTOID:
case ANYNONARRAYOID:
***************
*** 177,190 **** ProcedureCreate(const char *procedureName,
{
for (i = 0; i < allParamCount; i++)
{
! /*
! * We don't bother to distinguish input and output params here, so
! * if there is, say, just an input INTERNAL param then we will
! * still set internalOutParam. This is OK since we don't really
! * care.
! */
switch (allParams[i])
{
case ANYARRAYOID:
case ANYELEMENTOID:
case ANYNONARRAYOID:
--- 201,217 ----
{
for (i = 0; i < allParamCount; i++)
{
! if (modes == NULL ||
! (modes[i] != PROARGMODE_OUT &&
! modes[i] != PROARGMODE_INOUT &&
! modes[i] != PROARGMODE_TABLE))
! continue;
!
switch (allParams[i])
{
+ case ANYRANGEOID:
+ anyrangeOutParam = true;
+ /* FALL THROUGH */
case ANYARRAYOID:
case ANYELEMENTOID:
case ANYNONARRAYOID:
***************
*** 205,210 **** ProcedureCreate(const char *procedureName,
--- 232,244 ----
errmsg("cannot determine result data type"),
errdetail("A function returning a polymorphic type must have at least one polymorphic argument.")));
+ if ((returnType == ANYRANGEOID || anyrangeOutParam) &&
+ !anyrangeInParam)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+ errmsg("cannot determine result data type"),
+ errdetail("A function returning ANYRANGE must have at least one ANYRANGE argument.")));
+
if ((returnType == INTERNALOID || internalOutParam) && !internalInParam)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
***************
*** 225,248 **** ProcedureCreate(const char *procedureName,
procedureName,
format_type_be(parameterTypes->values[0]))));
! if (parameterModes != PointerGetDatum(NULL))
{
/*
- * We expect the array to be a 1-D CHAR array; verify that. We don't
- * need to use deconstruct_array() since the array data is just going
- * to look like a C array of char values.
- */
- ArrayType *modesArray = (ArrayType *) DatumGetPointer(parameterModes);
- char *modes;
-
- if (ARR_NDIM(modesArray) != 1 ||
- ARR_DIMS(modesArray)[0] != allParamCount ||
- ARR_HASNULL(modesArray) ||
- ARR_ELEMTYPE(modesArray) != CHAROID)
- elog(ERROR, "parameterModes is not a 1-D char array");
- modes = (char *) ARR_DATA_PTR(modesArray);
-
- /*
* Only the last input parameter can be variadic; if it is, save its
* element type. Errors here are just elog since caller should have
* checked this already.
--- 259,267 ----
procedureName,
format_type_be(parameterTypes->values[0]))));
! if (modes != NULL)
{
/*
* Only the last input parameter can be variadic; if it is, save its
* element type. Errors here are just elog since caller should have
* checked this already.
*** /dev/null
--- b/src/backend/catalog/pg_range.c
***************
*** 0 ****
--- 1,136 ----
+ /*-------------------------------------------------------------------------
+ *
+ * pg_range.c
+ * routines to support manipulation of the pg_range relation
+ *
+ * Copyright (c) 2006-2010, PostgreSQL Global Development Group
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/catalog/pg_range.c
+ *
+ *-------------------------------------------------------------------------
+ */
+ #include "postgres.h"
+
+ #include "access/genam.h"
+ #include "access/heapam.h"
+ #include "catalog/dependency.h"
+ #include "catalog/indexing.h"
+ #include "catalog/pg_collation.h"
+ #include "catalog/pg_opclass.h"
+ #include "catalog/pg_proc.h"
+ #include "catalog/pg_range.h"
+ #include "catalog/pg_type.h"
+ #include "utils/builtins.h"
+ #include "utils/fmgroids.h"
+ #include "utils/tqual.h"
+ #include "utils/rel.h"
+
+ /*
+ * RangeCreate
+ * Create an entry in pg_range.
+ */
+ void
+ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
+ Oid rangeSubOpclass, RegProcedure rangeCanonical,
+ RegProcedure 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
***************
*** 42,48 ****
--- 42,52 ----
#include "catalog/pg_constraint.h"
#include "catalog/pg_depend.h"
#include "catalog/pg_enum.h"
+ #include "catalog/pg_language.h"
#include "catalog/pg_namespace.h"
+ #include "catalog/pg_proc.h"
+ #include "catalog/pg_proc_fn.h"
+ #include "catalog/pg_range.h"
#include "catalog/pg_type.h"
#include "catalog/pg_type_fn.h"
#include "commands/defrem.h"
***************
*** 63,68 ****
--- 67,73 ----
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
+ #include "utils/rangetypes.h"
#include "utils/rel.h"
#include "utils/syscache.h"
#include "utils/tqual.h"
***************
*** 87,92 **** static Oid findTypeSendFunction(List *procname, Oid typeOid);
--- 92,100 ----
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,
--- 103,110 ----
Oid baseTypeOid,
int typMod, Constraint *constr,
char *domainName);
+ static void makeRangeConstructor(char *name, Oid namespace, Oid rettype,
+ Oid subtype);
/*
***************
*** 643,648 **** RemoveTypeById(Oid typeOid)
--- 653,666 ----
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);
***************
*** 1135,1140 **** DefineEnum(CreateEnumStmt *stmt)
--- 1153,1479 ----
}
/*
+ * 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;
+
+ AclResult aclresult;
+
+ /* Convert list of names to a name and namespace */
+ typeNamespace = QualifiedNameGetCreationNamespace(stmt->typeName,
+ &typeName);
+
+ /* Check we have creation rights in target namespace */
+ aclresult = pg_namespace_aclcheck(typeNamespace, GetUserId(), ACL_CREATE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
+ get_namespace_name(typeNamespace));
+
+ /*
+ * Look to see if type already exists (presumably as a shell; if not,
+ * TypeCreate will complain).
+ */
+ typoid = GetSysCacheOid2(TYPENAMENSP,
+ CStringGetDatum(typeName),
+ ObjectIdGetDatum(typeNamespace));
+
+ /*
+ * If it's not a shell, see if it's an autogenerated array type, and if so
+ * rename it out of the way.
+ */
+ if (OidIsValid(typoid) && get_typisdefined(typoid))
+ {
+ if (moveArrayTypeName(typoid, typeName, typeNamespace))
+ typoid = InvalidOid;
+ }
+
+ /*
+ * If it doesn't exist, create it as a shell, so that the OID is known for
+ * use in the I/O function definitions.
+ */
+ if (!OidIsValid(typoid))
+ {
+ typoid = TypeShellMake(typeName, typeNamespace, GetUserId());
+ /* Make new shell type visible for modification below */
+ CommandCounterIncrement();
+
+ /*
+ * If the command was a parameterless CREATE TYPE, we're done ---
+ * creating the shell type was all we're supposed to do.
+ */
+ if (parameters == NIL)
+ return;
+ }
+ else
+ {
+ /* Complain if dummy CREATE TYPE and entry already exists */
+ if (parameters == NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("type \"%s\" already exists", typeName)));
+ }
+
+ foreach(lc, stmt->params)
+ {
+ DefElem *defel = lfirst(lc);
+
+ if (pg_strcasecmp(defel->defname, "subtype") == 0)
+ {
+ if (OidIsValid(rangeSubtype))
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options")));
+ rangeSubtype = typenameTypeId(NULL, defGetTypeName(defel));
+ }
+ else if (pg_strcasecmp(defel->defname, "canonical") == 0)
+ {
+ if (OidIsValid(rangeCanonical))
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options")));
+ rangeCanonical = findRangeCanonicalFunction(
+ defGetQualifiedName(defel), typoid);
+ }
+ else if (pg_strcasecmp(defel->defname, "collation") == 0)
+ {
+ if (rangeCollationName != NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options")));
+ rangeCollationName = defGetQualifiedName(defel);
+ }
+ else if (pg_strcasecmp(defel->defname, "analyze") == 0)
+ {
+ if (OidIsValid(rangeAnalyze))
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options")));
+ rangeAnalyze = findTypeAnalyzeFunction(defGetQualifiedName(defel),
+ typoid);
+ }
+ else if (pg_strcasecmp(defel->defname, "subtype_opclass") == 0)
+ {
+ if (rangeSubOpclassName != NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options")));
+ rangeSubOpclassName = defGetQualifiedName(defel);
+ }
+ else if (pg_strcasecmp(defel->defname, "subtype_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);
+
+ makeRangeConstructor(typeName, typeNamespace, typoid, rangeSubtype);
+ }
+
+ /*
+ * Because there may exist several range types over one subtype, the range type
+ * can't be determined from the subtype. This means that constructors can't be
+ * polymorphic, and so we must generate a new constructor for every range type
+ * defined.
+ *
+ * We actually define 4 functions with 0 through 3 arguments. This is just to
+ * offer more convenience for the user.
+ */
+ static void
+ makeRangeConstructor(char *name, Oid namespace, Oid rangeOid, Oid subtype)
+ {
+ ObjectAddress referenced;
+ Oid constructorArgTypes[3];
+ int i;
+
+ referenced.classId = TypeRelationId;
+ referenced.objectId = rangeOid;
+ referenced.objectSubId = 0;
+
+ constructorArgTypes[0] = subtype;
+ constructorArgTypes[1] = subtype;
+ constructorArgTypes[2] = TEXTOID;
+
+ for (i = 0; i < 4; i++)
+ {
+ oidvector *constructorArgTypesVector;
+ ObjectAddress myself;
+ Oid procOid;
+ char *prosrc[4] = { "range_constructor0",
+ "range_constructor1",
+ "range_constructor2",
+ "range_constructor3"};
+
+ constructorArgTypesVector = buildoidvector(constructorArgTypes, i);
+
+ procOid = ProcedureCreate(
+ name, /* name */
+ namespace, /* namespace */
+ false, /* replace */
+ false, /* return set */
+ rangeOid, /* return type */
+ INTERNALlanguageId, /* language */
+ F_FMGR_INTERNAL_VALIDATOR, /* language validator */
+ prosrc[i], /* prosrc */
+ NULL, /* probin */
+ false, /* agg */
+ false, /* window */
+ false, /* security definer */
+ false, /* strict */
+ PROVOLATILE_IMMUTABLE, /* volatility */
+ constructorArgTypesVector, /* param types */
+ PointerGetDatum(NULL), /* allParameterTypes */
+ PointerGetDatum(NULL), /* parameterModes */
+ PointerGetDatum(NULL), /* parameterNames */
+ NIL, /* parameterDefaults */
+ PointerGetDatum(NULL), /* proconfig */
+ 1.0, /* procost */
+ 0.0); /* prorows */
+
+ /*
+ * Make the constructor internally-dependent on the range type so that
+ * the user doesn't have to treat them as separate objects.
+ */
+ myself.classId = ProcedureRelationId;
+ myself.objectId = procOid;
+ myself.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+ }
+ }
+
+ /*
* AlterEnum
* Adds a new label to an existing enum.
*/
***************
*** 1450,1455 **** findTypeAnalyzeFunction(List *procname, Oid typeOid)
--- 1789,1890 ----
}
/*
+ * Find named btree opclass for subtype, or default btree opclass if
+ * opcname is NIL. This will be used for comparing values of subtype.
+ */
+ static Oid
+ findRangeSubOpclass(List *opcname, Oid subtype)
+ {
+ Oid opcid;
+
+ if (opcname == NIL)
+ {
+ opcid = GetDefaultOpClass(subtype, BTREE_AM_OID);
+ if (!OidIsValid(opcid))
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("data type %s has no default operator class for access method \"btree\"",
+ format_type_be(subtype)),
+ errhint("You must specify an operator class for the data type or define a default operator class for the data type.")));
+ }
+ return opcid;
+ }
+
+ opcid = get_opclass_oid(BTREE_AM_OID, opcname, false);
+
+ return opcid;
+ }
+
+ /*
+ * Used to find a range's 'canonical' function.
+ */
+ static Oid
+ 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
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
***************
*** 3055,3060 **** _copyCreateEnumStmt(CreateEnumStmt *from)
--- 3055,3071 ----
return newnode;
}
+ static CreateRangeStmt *
+ _copyCreateRangeStmt(CreateRangeStmt *from)
+ {
+ CreateRangeStmt *newnode = makeNode(CreateRangeStmt);
+
+ COPY_NODE_FIELD(typeName);
+ COPY_NODE_FIELD(params);
+
+ return newnode;
+ }
+
static AlterEnumStmt *
_copyAlterEnumStmt(AlterEnumStmt *from)
{
***************
*** 4297,4302 **** copyObject(void *from)
--- 4308,4316 ----
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
***************
*** 4336,4341 **** DefineStmt:
--- 4336,4348 ----
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
***************
*** 158,164 **** coerce_type(ParseState *pstate, Node *node,
return node;
}
if (targetTypeId == ANYARRAYOID ||
! targetTypeId == ANYENUMOID)
{
/*
* Assume can_coerce_type verified that implicit coercion is okay.
--- 158,165 ----
return node;
}
if (targetTypeId == ANYARRAYOID ||
! targetTypeId == ANYENUMOID ||
! targetTypeId == ANYRANGEOID)
{
/*
* Assume can_coerce_type verified that implicit coercion is okay.
***************
*** 1275,1283 **** coerce_to_common_type(ParseState *pstate, Node *node,
* 1) All arguments declared ANYARRAY must have matching datatypes,
* and must in fact be varlena arrays.
* 2) All arguments declared ANYELEMENT must have matching datatypes.
! * 3) If there are arguments of both ANYELEMENT and ANYARRAY, make sure
! * the actual ANYELEMENT datatype is in fact the element type for
! * the actual ANYARRAY datatype.
* 4) ANYENUM is treated the same as ANYELEMENT except that if it is used
* (alone or in combination with plain ANYELEMENT), we add the extra
* condition that the ANYELEMENT type must be an enum.
--- 1276,1286 ----
* 1) All arguments declared ANYARRAY must have matching datatypes,
* and must in fact be varlena arrays.
* 2) All arguments declared ANYELEMENT must have matching datatypes.
! * 3) If there are arguments of both ANYELEMENT and ANYARRAY, make sure the
! * actual ANYELEMENT datatype is in fact the element type for the actual
! * ANYARRAY datatype. Similarly, if there are arguments of both ANYELEMENT
! * and ANYRANGE, make sure the actual ANYELEMENT datatype is in fact the
! * subtype for the actual ANYRANGE type.
* 4) ANYENUM is treated the same as ANYELEMENT except that if it is used
* (alone or in combination with plain ANYELEMENT), we add the extra
* condition that the ANYELEMENT type must be an enum.
***************
*** 1311,1316 **** 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;
***************
*** 1348,1353 **** 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 */
***************
*** 1393,1398 **** 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;
}
***************
*** 1416,1438 **** check_generic_type_consistency(Oid *actual_arg_types,
* if it is declared as a polymorphic type:
*
* 1) If return type is ANYARRAY, and any argument is ANYARRAY, use the
! * argument's actual type as the function's return type.
! * 2) If return type is ANYARRAY, no argument is ANYARRAY, but any argument
! * is ANYELEMENT, use the actual type of the argument to determine
! * the function's return type, i.e. the element type's corresponding
! * array type.
* 3) If return type is ANYARRAY, no argument is ANYARRAY or ANYELEMENT,
! * generate an ERROR. This condition is prevented by CREATE FUNCTION
! * and is therefore not expected here.
* 4) If return type is ANYELEMENT, and any argument is ANYELEMENT, use the
* argument's actual type as the function's return type.
! * 5) If return type is ANYELEMENT, no argument is ANYELEMENT, but any
! * argument is ANYARRAY, use the actual type of the argument to determine
! * the function's return type, i.e. the array type's corresponding
! * element type.
! * 6) If return type is ANYELEMENT, no argument is ANYARRAY or ANYELEMENT,
! * generate an ERROR. This condition is prevented by CREATE FUNCTION
! * and is therefore not expected here.
* 7) ANYENUM is treated the same as ANYELEMENT except that if it is used
* (alone or in combination with plain ANYELEMENT), we add the extra
* condition that the ANYELEMENT type must be an enum.
--- 1451,1478 ----
* if it is declared as a polymorphic type:
*
* 1) If return type is ANYARRAY, and any argument is ANYARRAY, use the
! * argument's actual type as the function's return type. Similarly, if
! * return type is ANYRANGE, and any argument is ANYRANGE, use the argument's
! * actual type as the function's return type.
! * 2) If return type is ANYARRAY, no argument is ANYARRAY, but any argument is
! * ANYELEMENT, use the actual type of the argument to determine the
! * function's return type, i.e. the element type's corresponding array
! * type. Note: similar behavior does not exist for ANYRANGE, because it's
! * impossble to determine the range type from the subtype alone.\
* 3) If return type is ANYARRAY, no argument is ANYARRAY or ANYELEMENT,
! * generate an ERROR. Similarly, if the return type is ANYRANGE, and no
! * argument is ANYRANGE or ANYELEMENT, generate an error. These conditions
! * are prevented by CREATE FUNCTION and is therefore not expected here.
* 4) If return type is ANYELEMENT, and any argument is ANYELEMENT, use the
* argument's actual type as the function's return type.
! * 5) If return type is ANYELEMENT, no argument is ANYELEMENT, but any argument
! * is ANYARRAY or ANYRANGE, use the actual type of the argument to determine
! * the function's return type; i.e. the array type's corresponding element
! * type or the range type's corresponding subtype (or both, in which case
! * they must match).
! * 6) If return type is ANYELEMENT, no argument is ANYARRAY, ANYRANGE, or
! * ANYELEMENT, generate an ERROR. This condition is prevented by CREATE
! * FUNCTION and is therefore not expected here.
* 7) ANYENUM is treated the same as ANYELEMENT except that if it is used
* (alone or in combination with plain ANYELEMENT), we add the extra
* condition that the ANYELEMENT type must be an enum.
***************
*** 1441,1449 **** check_generic_type_consistency(Oid *actual_arg_types,
* (This is a no-op if used in combination with ANYARRAY or ANYENUM, but
* is an extra restriction if not.)
*
! * Domains over arrays match ANYARRAY arguments, and are immediately flattened
! * to their base type. (In particular, if the return type is also ANYARRAY,
! * we'll set it to the base type not the domain type.)
*
* When allow_poly is false, we are not expecting any of the actual_arg_types
* to be polymorphic, and we should not return a polymorphic result type
--- 1481,1490 ----
* (This is a no-op if used in combination with ANYARRAY or ANYENUM, but
* is an extra restriction if not.)
*
! * Domains over arrays or ranges match ANYARRAY or ANYRANGE arguments,
! * respectively, and are immediately flattened to their base type. (In
! * particular, if the return type is also ANYARRAY or ANYRANGE, we'll set it to
! * the base type not the domain type.)
*
* When allow_poly is false, we are not expecting any of the actual_arg_types
* to be polymorphic, and we should not return a polymorphic result type
***************
*** 1473,1479 **** enforce_generic_type_consistency(Oid *actual_arg_types,
--- 1514,1522 ----
bool have_unknowns = false;
Oid elem_typeid = InvalidOid;
Oid array_typeid = InvalidOid;
+ Oid range_typeid = InvalidOid;
Oid array_typelem;
+ Oid range_typelem;
bool have_anyelement = (rettype == ANYELEMENTOID ||
rettype == ANYNONARRAYOID ||
rettype == ANYENUMOID);
***************
*** 1534,1539 **** enforce_generic_type_consistency(Oid *actual_arg_types,
--- 1577,1602 ----
format_type_be(actual_type))));
array_typeid = actual_type;
}
+ else if (decl_type == ANYRANGEOID)
+ {
+ have_generics = true;
+ if (actual_type == UNKNOWNOID)
+ {
+ have_unknowns = true;
+ continue;
+ }
+ if (allow_poly && decl_type == actual_type)
+ continue; /* no new information here */
+ actual_type = getBaseType(actual_type); /* flatten domains */
+ if (OidIsValid(range_typeid) && actual_type != range_typeid)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("arguments declared \"anyrange\" are not all alike"),
+ errdetail("%s versus %s",
+ format_type_be(range_typeid),
+ format_type_be(actual_type))));
+ range_typeid = actual_type;
+ }
}
/*
***************
*** 1579,1584 **** enforce_generic_type_consistency(Oid *actual_arg_types,
--- 1642,1675 ----
format_type_be(elem_typeid))));
}
}
+ /* Get the element type based on the range type, if we have one */
+ else if (OidIsValid(range_typeid))
+ {
+ range_typelem = get_range_subtype(range_typeid);
+ if (!OidIsValid(range_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared \"anyrange\" is not a range but type %s",
+ format_type_be(range_typeid))));
+
+ if (!OidIsValid(elem_typeid))
+ {
+ /*
+ * if we don't have an element type yet, use the one we just got
+ */
+ elem_typeid = range_typelem;
+ }
+ else if (range_typelem != elem_typeid)
+ {
+ /* otherwise, they better match */
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared \"anyrange\" is not consistent with argument declared \"anyelement\""),
+ errdetail("%s versus %s",
+ format_type_be(range_typeid),
+ format_type_be(elem_typeid))));
+ }
+ }
else if (!OidIsValid(elem_typeid))
{
if (allow_poly)
***************
*** 1645,1650 **** enforce_generic_type_consistency(Oid *actual_arg_types,
--- 1736,1752 ----
}
declared_arg_types[j] = array_typeid;
}
+ else if (decl_type == ANYRANGEOID)
+ {
+ if (!OidIsValid(range_typeid))
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find range type for data type %s",
+ format_type_be(elem_typeid))));
+ }
+ declared_arg_types[j] = range_typeid;
+ }
}
}
***************
*** 1663,1668 **** enforce_generic_type_consistency(Oid *actual_arg_types,
--- 1765,1783 ----
return array_typeid;
}
+ /* if we return ANYRANGE use the appropriate argument type */
+ if (rettype == ANYRANGEOID)
+ {
+ if (!OidIsValid(range_typeid))
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find range type for data type %s",
+ format_type_be(elem_typeid))));
+ }
+ return range_typeid;
+ }
+
/* if we return ANYELEMENT use the appropriate argument type */
if (rettype == ANYELEMENTOID ||
rettype == ANYNONARRAYOID ||
***************
*** 1711,1717 **** 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);
--- 1826,1833 ----
}
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);
***************
*** 1726,1732 **** resolve_generic_type(Oid declared_type,
}
else if (declared_type == ANYELEMENTOID ||
declared_type == ANYNONARRAYOID ||
! declared_type == ANYENUMOID)
{
if (context_declared_type == ANYARRAYOID)
{
--- 1842,1849 ----
}
else if (declared_type == ANYELEMENTOID ||
declared_type == ANYNONARRAYOID ||
! declared_type == ANYENUMOID ||
! declared_type == ANYRANGEOID)
{
if (context_declared_type == ANYARRAYOID)
{
***************
*** 1741,1746 **** resolve_generic_type(Oid declared_type,
--- 1858,1875 ----
format_type_be(context_base_type))));
return array_typelem;
}
+ else if (context_declared_type == ANYRANGEOID)
+ {
+ /* Use the element type corresponding to actual type */
+ Oid range_typelem = get_range_subtype(context_actual_type);
+
+ if (!OidIsValid(range_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared \"anyrange\" is not a range but type %s",
+ format_type_be(context_actual_type))));
+ return range_typelem;
+ }
else if (context_declared_type == ANYELEMENTOID ||
context_declared_type == ANYNONARRAYOID ||
context_declared_type == ANYENUMOID)
***************
*** 1854,1859 **** IsBinaryCoercible(Oid srctype, Oid targettype)
--- 1983,1993 ----
if (type_is_enum(srctype))
return true;
+ /* Also accept any range type as coercible to ANYRANGE */
+ if (targettype == ANYRANGEOID)
+ if (type_is_range(srctype))
+ return true;
+
/* Also accept any composite type as coercible to RECORD */
if (targettype == RECORDOID)
if (ISCOMPLEX(srctype))
*** 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:
***************
*** 870,875 **** standard_ProcessUtility(Node *parsetree,
--- 871,880 ----
DefineEnum((CreateEnumStmt *) parsetree);
break;
+ case T_CreateRangeStmt:
+ DefineRange((CreateRangeStmt *) parsetree);
+ break;
+
case T_AlterEnumStmt: /* ALTER TYPE (enum) */
/*
***************
*** 1854,1859 **** CreateCommandTag(Node *parsetree)
--- 1859,1868 ----
tag = "CREATE TYPE";
break;
+ case T_CreateRangeStmt:
+ tag = "CREATE TYPE";
+ break;
+
case T_AlterEnumStmt:
tag = "ALTER TYPE";
break;
***************
*** 2401,2406 **** GetCommandLogLevel(Node *parsetree)
--- 2410,2419 ----
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
***************
*** 889,894 **** date_timestamp(PG_FUNCTION_ARGS)
--- 889,901 ----
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,2082 ----
+ /*-------------------------------------------------------------------------
+ *
+ * rangetypes.c
+ * I/O functions, operators, and support functions for range types
+ *
+ * Copyright (c) 2006-2011, PostgreSQL Global Development Group
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/utils/adt/rangetypes.c
+ *
+ *-------------------------------------------------------------------------
+ */
+ #include "postgres.h"
+
+ #include "access/hash.h"
+ #include "access/nbtree.h"
+ #include "catalog/pg_opclass.h"
+ #include "catalog/pg_range.h"
+ #include "catalog/pg_type.h"
+ #include "fmgr.h"
+ #include "lib/stringinfo.h"
+ #include "libpq/pqformat.h"
+ #include "utils/builtins.h"
+ #include "utils/date.h"
+ #include "utils/fmgroids.h"
+ #include "utils/int8.h"
+ #include "utils/lsyscache.h"
+ #include "utils/rangetypes.h"
+ #include "utils/syscache.h"
+ #include "utils/typcache.h"
+
+ #define TYPE_IS_PACKABLE(typlen, typstorage) \
+ (typlen == -1 && typstorage != 'p')
+
+ /* flags */
+ #define RANGE_EMPTY 0x01
+ #define RANGE_LB_INC 0x02
+ #define RANGE_LB_NULL 0x04 /* NOT USED */
+ #define RANGE_LB_INF 0x08
+ #define RANGE_UB_INC 0x10
+ #define RANGE_UB_NULL 0x20 /* NOT USED */
+ #define RANGE_UB_INF 0x40
+
+ #define RANGE_HAS_LBOUND(flags) (!(flags & (RANGE_EMPTY | \
+ RANGE_LB_NULL | \
+ RANGE_LB_INF)))
+
+ #define RANGE_HAS_UBOUND(flags) (!(flags & (RANGE_EMPTY | \
+ RANGE_UB_NULL | \
+ RANGE_UB_INF)))
+
+ #define RANGE_EMPTY_LITERAL "empty"
+
+ static void range_parse(char *input_str, char *flags, char **lbound_str,
+ char **ubound_str);
+ static char *range_parse_bound(char *string, char *ptr, char **bound_str,
+ bool *infinite);
+ static char *range_deparse(char flags, char *lbound_str, char *ubound_str);
+ static char *range_bound_escape(char *in_str);
+ static bool range_contains_internal(FunctionCallInfo fcinfo, RangeType *r1,
+ RangeType *r2);
+ static Size datum_compute_size(Size sz, Datum datum, bool typbyval,
+ char typalign, int16 typlen, char typstorage);
+ static Pointer datum_write(Pointer ptr, Datum datum, bool typbyval,
+ char typalign, int16 typlen, char typstorage);
+
+ /*
+ *----------------------------------------------------------
+ * I/O FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+ Datum
+ range_in(PG_FUNCTION_ARGS)
+ {
+ char *input_str = PG_GETARG_CSTRING(0);
+ Oid rngtypoid = PG_GETARG_OID(1);
+ Oid typmod = PG_GETARG_INT32(2);
+
+ char flags;
+ Datum range;
+ char *lbound_str;
+ char *ubound_str;
+
+ regproc subInput;
+ FmgrInfo subInputFn;
+ Oid ioParam;
+
+ RangeTypeInfo rngtypinfo;
+ RangeBound lower;
+ RangeBound upper;
+
+ if (rngtypoid == ANYRANGEOID)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot accept a value of type anyrange")));
+
+ range_gettypinfo(fcinfo, rngtypoid, &rngtypinfo);
+
+ /* parse */
+ range_parse(input_str, &flags, &lbound_str, &ubound_str);
+
+ /* input */
+ getTypeInputInfo(rngtypinfo.subtype, &subInput, &ioParam);
+ fmgr_info(subInput, &subInputFn);
+
+ lower.rngtypid = rngtypoid;
+ lower.infinite = (flags & RANGE_LB_INF) != 0;
+ lower.inclusive = (flags & RANGE_LB_INC) != 0;
+ lower.lower = true;
+ upper.rngtypid = rngtypoid;
+ upper.infinite = (flags & RANGE_UB_INF) != 0;
+ upper.inclusive = (flags & RANGE_UB_INC) != 0;
+ upper.lower = false;
+
+ if (RANGE_HAS_LBOUND(flags))
+ lower.val = InputFunctionCall(&subInputFn, lbound_str,
+ ioParam, typmod);
+ if (RANGE_HAS_UBOUND(flags))
+ upper.val = InputFunctionCall(&subInputFn, ubound_str,
+ ioParam, typmod);
+
+ /* serialize and canonicalize */
+ range = make_range(fcinfo, &lower, &upper, flags & RANGE_EMPTY);
+
+ PG_RETURN_RANGE(range);
+ }
+
+ Datum
+ range_out(PG_FUNCTION_ARGS)
+ {
+ RangeType *range = PG_GETARG_RANGE(0);
+
+ regproc subOutput;
+ FmgrInfo subOutputFn;
+ bool isVarlena;
+
+ char flags = 0;
+ char *lbound_str = NULL;
+ char *ubound_str = NULL;
+ char *output_str;
+
+ bool empty;
+
+ RangeTypeInfo rngtypinfo;
+ RangeBound lower;
+ RangeBound upper;
+
+ /* deserialize */
+ range_deserialize(fcinfo, range, &lower, &upper, &empty);
+
+ if (lower.rngtypid != upper.rngtypid)
+ elog(ERROR, "range types do not match");
+
+ range_gettypinfo(fcinfo, lower.rngtypid, &rngtypinfo);
+
+ if (empty)
+ flags |= RANGE_EMPTY;
+
+ flags |= (lower.inclusive) ? RANGE_LB_INC : 0;
+ flags |= (lower.infinite) ? RANGE_LB_INF : 0;
+ flags |= (upper.inclusive) ? RANGE_UB_INC : 0;
+ flags |= (upper.infinite) ? RANGE_UB_INF : 0;
+
+ /* output */
+ getTypeOutputInfo(rngtypinfo.subtype, &subOutput, &isVarlena);
+ fmgr_info(subOutput, &subOutputFn);
+
+ if (RANGE_HAS_LBOUND(flags))
+ lbound_str = OutputFunctionCall(&subOutputFn, lower.val);
+ if (RANGE_HAS_UBOUND(flags))
+ ubound_str = OutputFunctionCall(&subOutputFn, upper.val);
+
+ /* deparse */
+ output_str = range_deparse(flags, lbound_str, ubound_str);
+
+ PG_RETURN_CSTRING(output_str);
+ }
+
+ /*
+ * Binary representation: The first byte is the flags, then 4 bytes are the
+ * range type Oid, then the lower bound (if present) then the upper bound (if
+ * present). Each bound is represented by a 4-byte length header and the binary
+ * representation of that bound (as returned by a call to the send function for
+ * the subtype).
+ */
+
+ Datum
+ range_recv(PG_FUNCTION_ARGS)
+ {
+ StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
+ Oid rngtypid = PG_GETARG_OID(1);
+ int32 typmod = PG_GETARG_INT32(2);
+ Oid subrecv;
+ Oid ioparam;
+ Datum range;
+ char flags;
+ RangeBound lower;
+ RangeBound upper;
+
+ RangeTypeInfo rngtypinfo;
+
+ flags = (unsigned char) pq_getmsgbyte(buf);
+
+ range_gettypinfo(fcinfo, rngtypid, &rngtypinfo);
+
+ getTypeBinaryInputInfo(rngtypinfo.subtype, &subrecv, &ioparam);
+
+ if (RANGE_HAS_LBOUND(flags))
+ {
+ uint32 bound_len = pq_getmsgint(buf, 4);
+ const char *bound_data = pq_getmsgbytes(buf, bound_len);
+ StringInfoData bound_buf;
+
+ initStringInfo(&bound_buf);
+ appendBinaryStringInfo(&bound_buf, bound_data, bound_len);
+
+ lower.val = OidReceiveFunctionCall(subrecv,
+ &bound_buf,
+ ioparam,
+ typmod);
+ pfree(bound_buf.data);
+ }
+ else
+ lower.val = (Datum) 0;
+
+ if (RANGE_HAS_UBOUND(flags))
+ {
+ uint32 bound_len = pq_getmsgint(buf, 4);
+ const char *bound_data = pq_getmsgbytes(buf, bound_len);
+ StringInfoData bound_buf;
+
+ initStringInfo(&bound_buf);
+ appendBinaryStringInfo(&bound_buf, bound_data, bound_len);
+
+ upper.val = OidReceiveFunctionCall(subrecv,
+ &bound_buf,
+ ioparam,
+ typmod);
+ pfree(bound_buf.data);
+ }
+ else
+ upper.val = (Datum) 0;
+
+ pq_getmsgend(buf);
+
+ lower.rngtypid = rngtypid;
+ lower.infinite = (flags & RANGE_LB_INF) != 0;
+ lower.inclusive = (flags & RANGE_LB_INC) != 0;
+ lower.lower = true;
+ upper.rngtypid = rngtypid;
+ upper.infinite = (flags & RANGE_UB_INF) != 0;
+ upper.inclusive = (flags & RANGE_UB_INC) != 0;
+ upper.lower = false;
+
+ /* serialize and canonicalize */
+ range = make_range(fcinfo, &lower, &upper, flags & RANGE_EMPTY);
+
+ /*
+ * XXX if the subtype is pass-by-val, we should pfree the upper and
+ * lower bounds here.
+ */
+
+ PG_RETURN_RANGE(range);
+ }
+
+ Datum
+ range_send(PG_FUNCTION_ARGS)
+ {
+ RangeType *range = PG_GETARG_RANGE(0);
+ StringInfo buf = makeStringInfo();
+ char flags = 0;
+ RangeBound lower;
+ RangeBound upper;
+ bool empty;
+ Oid subsend;
+ bool typIsVarlena;
+
+ RangeTypeInfo rngtypinfo;
+
+ pq_begintypsend(buf);
+
+ range_deserialize(fcinfo, range, &lower, &upper, &empty);
+
+ if (empty)
+ flags |= RANGE_EMPTY;
+
+ flags |= (lower.inclusive) ? RANGE_LB_INC : 0;
+ flags |= (lower.infinite) ? RANGE_LB_INF : 0;
+ flags |= (upper.inclusive) ? RANGE_UB_INC : 0;
+ flags |= (upper.infinite) ? RANGE_UB_INF : 0;
+
+ range_gettypinfo(fcinfo, lower.rngtypid, &rngtypinfo);
+
+ getTypeBinaryOutputInfo(rngtypinfo.subtype,
+ &subsend, &typIsVarlena);
+
+ pq_sendbyte(buf, flags);
+
+ if (RANGE_HAS_LBOUND(flags))
+ {
+ Datum bound = PointerGetDatum(
+ OidSendFunctionCall(subsend, lower.val));
+ uint32 bound_len = VARSIZE(bound) - VARHDRSZ;
+ char *bound_data = VARDATA(bound);
+
+ pq_sendint(buf, bound_len, 4);
+ pq_sendbytes(buf, bound_data, bound_len);
+ }
+
+ if (RANGE_HAS_UBOUND(flags))
+ {
+ Datum bound = PointerGetDatum(
+ OidSendFunctionCall(subsend, upper.val));
+ uint32 bound_len = VARSIZE(bound) - VARHDRSZ;
+ char *bound_data = VARDATA(bound);
+
+ pq_sendint(buf, bound_len, 4);
+ pq_sendbytes(buf, bound_data, bound_len);
+ }
+
+ PG_RETURN_BYTEA_P(pq_endtypsend(buf));
+ }
+
+
+ /*
+ *----------------------------------------------------------
+ * GENERIC FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+ Datum
+ range_constructor0(PG_FUNCTION_ARGS)
+ {
+ Oid rngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ RangeType *range;
+ RangeBound lower;
+ RangeBound upper;
+
+ lower.rngtypid = rngtypid;
+ lower.val = (Datum) 0;
+ lower.inclusive = false;
+ lower.infinite = false;
+ lower.lower = true;
+
+ upper.rngtypid = rngtypid;
+ upper.val = (Datum) 0;
+ upper.inclusive = false;
+ upper.infinite = false;
+ upper.lower = false;
+
+ range = DatumGetRangeType(make_range(fcinfo, &lower, &upper, true));
+
+ PG_RETURN_RANGE(range);
+ }
+
+ Datum
+ range_constructor1(PG_FUNCTION_ARGS)
+ {
+ Datum arg1 = PG_GETARG_DATUM(0);
+ Oid rngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ RangeType *range;
+ RangeBound lower;
+ RangeBound upper;
+
+ if (PG_ARGISNULL(0))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATA_EXCEPTION),
+ errmsg("argument must not be NULL")));
+
+ lower.rngtypid = rngtypid;
+ lower.val = arg1;
+ lower.inclusive = true;
+ lower.infinite = false;
+ lower.lower = true;
+
+ upper.rngtypid = rngtypid;
+ upper.val = arg1;
+ upper.inclusive = true;
+ upper.infinite = false;
+ upper.lower = false;
+
+ range = DatumGetRangeType(make_range(fcinfo, &lower, &upper, false));
+
+ PG_RETURN_RANGE(range);
+ }
+
+ Datum
+ range_constructor2(PG_FUNCTION_ARGS)
+ {
+ Datum arg1 = PG_GETARG_DATUM(0);
+ Datum arg2 = PG_GETARG_DATUM(1);
+ Oid rngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ RangeType *range;
+ RangeBound lower;
+ RangeBound upper;
+ char flags;
+
+ flags = range_parse_flags(RANGE_DEFAULT_FLAGS);
+
+ lower.rngtypid = rngtypid;
+ lower.val = PG_ARGISNULL(0) ? (Datum)0 : arg1;
+ lower.inclusive = flags & RANGE_LB_INC;
+ lower.infinite = PG_ARGISNULL(0);
+ lower.lower = true;
+
+ upper.rngtypid = rngtypid;
+ upper.val = PG_ARGISNULL(1) ? (Datum)0 : arg2;
+ upper.inclusive = flags & RANGE_UB_INC;
+ upper.infinite = PG_ARGISNULL(1);
+ upper.lower = false;
+
+ range = DatumGetRangeType(make_range(fcinfo, &lower, &upper, false));
+
+ PG_RETURN_RANGE(range);
+ }
+
+ Datum
+ range_constructor3(PG_FUNCTION_ARGS)
+ {
+ Datum arg1 = PG_GETARG_DATUM(0);
+ Datum arg2 = PG_GETARG_DATUM(1);
+ Oid rngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ RangeType *range;
+ RangeBound lower;
+ RangeBound upper;
+ char flags;
+
+ if (PG_ARGISNULL(2))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATA_EXCEPTION),
+ errmsg("flags argument must not be NULL")));
+ flags = range_parse_flags(text_to_cstring(PG_GETARG_TEXT_P(2)));
+
+ lower.rngtypid = rngtypid;
+ lower.val = PG_ARGISNULL(0) ? (Datum)0 : arg1;
+ lower.inclusive = flags & RANGE_LB_INC;
+ lower.infinite = PG_ARGISNULL(0);
+ lower.lower = true;
+
+ upper.rngtypid = rngtypid;
+ upper.val = PG_ARGISNULL(1) ? (Datum)0 : arg2;
+ upper.inclusive = flags & RANGE_UB_INC;
+ upper.infinite = PG_ARGISNULL(1);
+ upper.lower = false;
+
+ range = DatumGetRangeType(make_range(fcinfo, &lower, &upper, false));
+
+ PG_RETURN_RANGE(range);
+ }
+
+ /* range -> subtype */
+ Datum
+ range_lower(PG_FUNCTION_ARGS)
+ {
+ RangeType *r1 = PG_GETARG_RANGE(0);
+ RangeBound lower;
+ RangeBound upper;
+ bool empty;
+
+ range_deserialize(fcinfo, r1, &lower, &upper, &empty);
+
+ if (empty)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATA_EXCEPTION),
+ errmsg("empty range has no lower bound")));
+ if (lower.infinite)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATA_EXCEPTION),
+ errmsg("range lower bound is infinite")));
+
+ PG_RETURN_DATUM(lower.val);
+ }
+
+ Datum
+ range_upper(PG_FUNCTION_ARGS)
+ {
+ RangeType *r1 = PG_GETARG_RANGE(0);
+ RangeBound lower;
+ RangeBound upper;
+ bool empty;
+
+ range_deserialize(fcinfo, r1, &lower, &upper, &empty);
+
+ if (empty)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATA_EXCEPTION),
+ errmsg("empty range has no upper bound")));
+ if (upper.infinite)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATA_EXCEPTION),
+ errmsg("range upper bound is infinite")));
+
+ PG_RETURN_DATUM(upper.val);
+ }
+
+
+ /* range -> bool */
+ Datum
+ range_empty(PG_FUNCTION_ARGS)
+ {
+ RangeType *r1 = PG_GETARG_RANGE(0);
+ RangeBound lower;
+ RangeBound upper;
+ bool empty;
+
+ range_deserialize(fcinfo, r1, &lower, &upper, &empty);
+
+ PG_RETURN_BOOL(empty);
+ }
+
+ Datum
+ range_non_empty(PG_FUNCTION_ARGS)
+ {
+ RangeType *r1 = PG_GETARG_RANGE(0);
+ RangeBound lower;
+ RangeBound upper;
+ bool empty;
+
+ range_deserialize(fcinfo, r1, &lower, &upper, &empty);
+
+ PG_RETURN_BOOL(!empty);
+ }
+
+ Datum
+ range_lower_inc(PG_FUNCTION_ARGS)
+ {
+ RangeType *r1 = PG_GETARG_RANGE(0);
+ RangeBound lower;
+ RangeBound upper;
+ bool empty;
+
+ range_deserialize(fcinfo, r1, &lower, &upper, &empty);
+
+ PG_RETURN_BOOL(lower.inclusive);
+ }
+
+ Datum
+ range_upper_inc(PG_FUNCTION_ARGS)
+ {
+ RangeType *r1 = PG_GETARG_RANGE(0);
+ RangeBound lower;
+ RangeBound upper;
+ bool empty;
+
+ range_deserialize(fcinfo, r1, &lower, &upper, &empty);
+
+ PG_RETURN_BOOL(upper.inclusive);
+ }
+
+ Datum
+ range_lower_inf(PG_FUNCTION_ARGS)
+ {
+ RangeType *r1 = PG_GETARG_RANGE(0);
+ RangeBound lower;
+ RangeBound upper;
+ bool empty;
+
+ range_deserialize(fcinfo, r1, &lower, &upper, &empty);
+
+ PG_RETURN_BOOL(lower.infinite);
+ }
+
+ Datum
+ range_upper_inf(PG_FUNCTION_ARGS)
+ {
+ RangeType *r1 = PG_GETARG_RANGE(0);
+ RangeBound lower;
+ RangeBound upper;
+ bool empty;
+
+ range_deserialize(fcinfo, r1, &lower, &upper, &empty);
+
+ PG_RETURN_BOOL(upper.infinite);
+ }
+
+
+ /* range, range -> bool */
+ Datum
+ range_eq(PG_FUNCTION_ARGS)
+ {
+ RangeType *r1 = PG_GETARG_RANGE(0);
+ RangeType *r2 = PG_GETARG_RANGE(1);
+
+ RangeBound lower1, lower2;
+ RangeBound upper1, upper2;
+ bool empty1, empty2;
+
+ range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
+ range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
+
+ if (lower1.rngtypid != upper1.rngtypid ||
+ lower1.rngtypid != lower2.rngtypid ||
+ lower1.rngtypid != upper2.rngtypid)
+ elog(ERROR, "range types do not match");
+
+ if (empty1 && empty2)
+ PG_RETURN_BOOL(true);
+ if (empty1 != empty2)
+ PG_RETURN_BOOL(false);
+
+ if (range_cmp_bounds(fcinfo, &lower1, &lower2) != 0)
+ PG_RETURN_BOOL(false);
+
+ if (range_cmp_bounds(fcinfo, &upper1, &upper2) != 0)
+ PG_RETURN_BOOL(false);
+
+ PG_RETURN_BOOL(true);
+ }
+
+ Datum
+ range_ne(PG_FUNCTION_ARGS)
+ {
+ bool eq = DatumGetBool(range_eq(fcinfo));
+
+ PG_RETURN_BOOL(!eq);
+ }
+
+ Datum
+ range_contains_elem(PG_FUNCTION_ARGS)
+ {
+ RangeType *r1 = PG_GETARG_RANGE(0);
+ RangeType *r2;
+ Datum val = PG_GETARG_DATUM(1);
+
+ RangeBound lower1, lower2;
+ RangeBound upper1, upper2;
+ bool empty1;
+
+ range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
+
+ lower2.rngtypid = lower1.rngtypid;
+ lower2.inclusive = true;
+ lower2.infinite = false;
+ lower2.lower = true;
+ lower2.val = val;
+
+ upper2.rngtypid = lower1.rngtypid;
+ upper2.inclusive = true;
+ upper2.infinite = false;
+ upper2.lower = false;
+ upper2.val = val;
+
+ r2 = DatumGetRangeType(make_range(fcinfo, &lower2, &upper2, false));
+
+ PG_RETURN_BOOL(range_contains_internal(fcinfo, r1, r2));
+ }
+
+ Datum
+ range_contains(PG_FUNCTION_ARGS)
+ {
+ RangeType *r1 = PG_GETARG_RANGE(0);
+ RangeType *r2 = PG_GETARG_RANGE(1);
+
+ PG_RETURN_BOOL(range_contains_internal(fcinfo, r1, r2));
+ }
+
+ Datum
+ elem_contained_by_range(PG_FUNCTION_ARGS)
+ {
+ RangeType *r1 = PG_GETARG_RANGE(1);
+ RangeType *r2;
+ Datum val = PG_GETARG_DATUM(0);
+
+ RangeBound lower1, lower2;
+ RangeBound upper1, upper2;
+ bool empty1;
+
+ range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
+
+ lower2.rngtypid = lower1.rngtypid;
+ lower2.inclusive = true;
+ lower2.infinite = false;
+ lower2.lower = true;
+ lower2.val = val;
+
+ upper2.rngtypid = lower1.rngtypid;
+ upper2.inclusive = true;
+ upper2.infinite = false;
+ upper2.lower = false;
+ upper2.val = val;
+
+ r2 = DatumGetRangeType(make_range(fcinfo, &lower2, &upper2, false));
+
+ PG_RETURN_BOOL(range_contains_internal(fcinfo, r1, r2));
+ }
+
+ Datum
+ range_contained_by(PG_FUNCTION_ARGS)
+ {
+ RangeType *r1 = PG_GETARG_RANGE(0);
+ RangeType *r2 = PG_GETARG_RANGE(1);
+
+ PG_RETURN_BOOL(range_contains_internal(fcinfo, r2, r1));
+ }
+
+ Datum
+ range_before(PG_FUNCTION_ARGS)
+ {
+ RangeType *r1 = PG_GETARG_RANGE(0);
+ RangeType *r2 = PG_GETARG_RANGE(1);
+
+ RangeBound lower1, lower2;
+ RangeBound upper1, upper2;
+ bool empty1, empty2;
+
+ range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
+ range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
+
+ if (lower1.rngtypid != upper1.rngtypid ||
+ lower1.rngtypid != lower2.rngtypid ||
+ lower1.rngtypid != upper2.rngtypid)
+ elog(ERROR, "range types do not match");
+
+ if (empty1 || empty2)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATA_EXCEPTION),
+ errmsg("input range is empty")));
+
+ if (range_cmp_bounds(fcinfo, &upper1, &lower2) < 0)
+ PG_RETURN_BOOL(true);
+ else
+ PG_RETURN_BOOL(false);
+ }
+
+ Datum
+ range_after(PG_FUNCTION_ARGS)
+ {
+ RangeType *r1 = PG_GETARG_RANGE(0);
+ RangeType *r2 = PG_GETARG_RANGE(1);
+
+ RangeBound lower1, lower2;
+ RangeBound upper1, upper2;
+ bool empty1, empty2;
+
+ range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
+ range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
+
+ if (lower1.rngtypid != upper1.rngtypid ||
+ lower1.rngtypid != lower2.rngtypid ||
+ lower1.rngtypid != upper2.rngtypid)
+ elog(ERROR, "range types do not match");
+
+ if (empty1 || empty2)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATA_EXCEPTION),
+ errmsg("input range is empty")));
+
+ if (range_cmp_bounds(fcinfo, &lower1, &upper2) > 0)
+ PG_RETURN_BOOL(true);
+ else
+ PG_RETURN_BOOL(false);
+ }
+
+ Datum range_adjacent(PG_FUNCTION_ARGS)
+ {
+ RangeType *r1 = PG_GETARG_RANGE(0);
+ RangeType *r2 = PG_GETARG_RANGE(1);
+
+ RangeTypeInfo rngtypinfo;
+
+ RangeBound lower1, lower2;
+ RangeBound upper1, upper2;
+ bool empty1, empty2;
+
+ range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
+ range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
+
+ if (lower1.rngtypid != upper1.rngtypid ||
+ lower1.rngtypid != lower2.rngtypid ||
+ lower1.rngtypid != upper2.rngtypid)
+ elog(ERROR, "range types do not match");
+
+ if (empty1 || empty2)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATA_EXCEPTION),
+ errmsg("undefined for empty ranges")));
+
+ /*
+ * For two ranges to be adjacent, the lower boundary of one range
+ * has to match the upper boundary of the other. However, the
+ * inclusivity of those two boundaries must also be different.
+ *
+ * The semantics for range_cmp_bounds aren't quite what we need
+ * here, so we do the comparison more directly.
+ */
+
+ range_gettypinfo(fcinfo, lower1.rngtypid, &rngtypinfo);
+
+ if (lower1.inclusive != upper2.inclusive)
+ {
+ if (DatumGetInt32(FunctionCall2Coll(&rngtypinfo.cmpFn,
+ rngtypinfo.collation,
+ lower1.val, upper2.val)) == 0)
+ PG_RETURN_BOOL(true);
+ }
+
+ if (upper1.inclusive != lower2.inclusive)
+ {
+ if (DatumGetInt32(FunctionCall2Coll(&rngtypinfo.cmpFn,
+ rngtypinfo.collation,
+ upper1.val, lower2.val)) == 0)
+ PG_RETURN_BOOL(true);
+ }
+
+ PG_RETURN_BOOL(false);
+ }
+
+ Datum
+ range_overlaps(PG_FUNCTION_ARGS)
+ {
+ RangeType *r1 = PG_GETARG_RANGE(0);
+ RangeType *r2 = PG_GETARG_RANGE(1);
+
+ RangeBound lower1, lower2;
+ RangeBound upper1, upper2;
+ bool empty1, empty2;
+
+ range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
+ range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
+
+ if (lower1.rngtypid != upper1.rngtypid ||
+ lower1.rngtypid != lower2.rngtypid ||
+ lower1.rngtypid != upper2.rngtypid)
+ elog(ERROR, "range types do not match");
+
+ if (empty1 || empty2)
+ PG_RETURN_BOOL(false);
+
+ if (range_cmp_bounds(fcinfo, &lower1, &lower2) >= 0 &&
+ range_cmp_bounds(fcinfo, &lower1, &upper2) <= 0)
+ PG_RETURN_BOOL(true);
+
+ if (range_cmp_bounds(fcinfo, &lower2, &lower1) >= 0 &&
+ range_cmp_bounds(fcinfo, &lower2, &upper1) <= 0)
+ PG_RETURN_BOOL(true);
+
+ PG_RETURN_BOOL(false);
+ }
+
+ Datum
+ range_overleft(PG_FUNCTION_ARGS)
+ {
+ RangeType *r1 = PG_GETARG_RANGE(0);
+ RangeType *r2 = PG_GETARG_RANGE(1);
+
+ RangeBound lower1, lower2;
+ RangeBound upper1, upper2;
+ bool empty1, empty2;
+
+ range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
+ range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
+
+ if (lower1.rngtypid != upper1.rngtypid ||
+ lower1.rngtypid != lower2.rngtypid ||
+ lower1.rngtypid != upper2.rngtypid)
+ elog(ERROR, "range types do not match");
+
+ if (empty1 || empty2)
+ PG_RETURN_BOOL(false);
+
+ if (range_cmp_bounds(fcinfo, &upper1, &upper2) <= 0)
+ PG_RETURN_BOOL(true);
+
+ PG_RETURN_BOOL(false);
+ }
+
+ Datum
+ range_overright(PG_FUNCTION_ARGS)
+ {
+ RangeType *r1 = PG_GETARG_RANGE(0);
+ RangeType *r2 = PG_GETARG_RANGE(1);
+
+ RangeBound lower1, lower2;
+ RangeBound upper1, upper2;
+ bool empty1, empty2;
+
+ range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
+ range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
+
+ if (lower1.rngtypid != upper1.rngtypid ||
+ lower1.rngtypid != lower2.rngtypid ||
+ lower1.rngtypid != upper2.rngtypid)
+ elog(ERROR, "range types do not match");
+
+ if (empty1 || empty2)
+ PG_RETURN_BOOL(false);
+
+ if (range_cmp_bounds(fcinfo, &lower1, &lower2) >= 0)
+ PG_RETURN_BOOL(true);
+
+ PG_RETURN_BOOL(false);
+ }
+
+
+ /* range, range -> range */
+ Datum
+ range_minus(PG_FUNCTION_ARGS)
+ {
+ RangeType *r1 = PG_GETARG_RANGE(0);
+ RangeType *r2 = PG_GETARG_RANGE(1);
+
+ RangeBound lower1, lower2;
+ RangeBound upper1, upper2;
+ bool empty1, empty2;
+
+ int cmp_l1l2, cmp_l1u2, cmp_u1l2, cmp_u1u2;
+
+ range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
+ range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
+
+ if (lower1.rngtypid != upper1.rngtypid ||
+ lower1.rngtypid != lower2.rngtypid ||
+ lower1.rngtypid != upper2.rngtypid)
+ elog(ERROR, "range types do not match");
+
+ if (empty1 || empty2)
+ PG_RETURN_RANGE(r1);
+
+ cmp_l1l2 = range_cmp_bounds(fcinfo, &lower1, &lower2);
+ cmp_l1u2 = range_cmp_bounds(fcinfo, &lower1, &upper2);
+ cmp_u1l2 = range_cmp_bounds(fcinfo, &upper1, &lower2);
+ cmp_u1u2 = range_cmp_bounds(fcinfo, &upper1, &upper2);
+
+ if (cmp_l1l2 < 0 && cmp_u1u2 > 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATA_EXCEPTION),
+ errmsg("result range is not contiguous")));
+
+ if (cmp_l1u2 > 0 || cmp_u1l2 < 0)
+ PG_RETURN_RANGE(r1);
+
+ if (cmp_l1l2 >= 0 && cmp_u1u2 <= 0)
+ PG_RETURN_RANGE(make_empty_range(fcinfo, lower1.rngtypid));
+
+ if (cmp_l1l2 <= 0 && cmp_u1l2 >= 0 && cmp_u1u2 <= 0)
+ {
+ lower2.inclusive = !lower2.inclusive;
+ lower2.lower = false; /* it will become the upper bound */
+ PG_RETURN_RANGE(make_range(fcinfo, &lower1, &lower2, false));
+ }
+
+ if (cmp_l1l2 >= 0 && cmp_u1u2 >= 0 && cmp_l1u2 <= 0)
+ {
+ upper2.inclusive = !upper2.inclusive;
+ upper2.lower = true; /* it will become the lower bound */
+ PG_RETURN_RANGE(make_range(fcinfo, &upper2, &upper1, false));
+ }
+
+ elog(ERROR, "unexpected error in range_minus");
+ PG_RETURN_VOID();
+ }
+
+ Datum
+ range_union(PG_FUNCTION_ARGS)
+ {
+ RangeType *r1 = PG_GETARG_RANGE(0);
+ RangeType *r2 = PG_GETARG_RANGE(1);
+
+ RangeBound lower1, lower2;
+ RangeBound upper1, upper2;
+ bool empty1, empty2;
+ RangeBound *result_lower;
+ RangeBound *result_upper;
+
+ range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
+ range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
+
+ if (empty1)
+ PG_RETURN_RANGE(r2);
+ if (empty2)
+ PG_RETURN_RANGE(r1);
+
+ if (!DatumGetBool(range_overlaps(fcinfo)) &&
+ !DatumGetBool(range_adjacent(fcinfo)))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATA_EXCEPTION),
+ errmsg("result range is not contiguous")));
+
+ if (range_cmp_bounds(fcinfo, &lower1, &lower2) < 0)
+ result_lower = &lower1;
+ else
+ result_lower = &lower2;
+
+ if (range_cmp_bounds(fcinfo, &upper1, &upper2) > 0)
+ result_upper = &upper1;
+ else
+ result_upper = &upper2;
+
+ PG_RETURN_RANGE(make_range(fcinfo, result_lower, result_upper, false));
+ }
+
+ Datum
+ range_intersect(PG_FUNCTION_ARGS)
+ {
+ RangeType *r1 = PG_GETARG_RANGE(0);
+ RangeType *r2 = PG_GETARG_RANGE(1);
+
+ RangeBound lower1, lower2;
+ RangeBound upper1, upper2;
+ bool empty1, empty2;
+ RangeBound *result_lower;
+ RangeBound *result_upper;
+
+ range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
+ range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
+
+ if (empty1 || empty2 || !DatumGetBool(range_overlaps(fcinfo)))
+ PG_RETURN_RANGE(make_empty_range(fcinfo, lower1.rngtypid));
+
+ if (range_cmp_bounds(fcinfo, &lower1, &lower2) >= 0)
+ result_lower = &lower1;
+ else
+ result_lower = &lower2;
+
+ if (range_cmp_bounds(fcinfo, &upper1, &upper2) <= 0)
+ result_upper = &upper1;
+ else
+ result_upper = &upper2;
+
+ PG_RETURN_RANGE(make_range(fcinfo, result_lower, result_upper, false));
+ }
+
+ /* Btree support */
+
+ Datum
+ range_cmp(PG_FUNCTION_ARGS)
+ {
+ RangeType *r1 = PG_GETARG_RANGE(0);
+ RangeType *r2 = PG_GETARG_RANGE(1);
+
+ RangeBound lower1, lower2;
+ RangeBound upper1, upper2;
+ bool empty1, empty2;
+
+ int cmp;
+
+ range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
+ range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
+
+ if (lower1.rngtypid != upper1.rngtypid ||
+ lower1.rngtypid != lower2.rngtypid ||
+ lower1.rngtypid != upper2.rngtypid)
+ elog(ERROR, "range types do not match");
+
+ if (empty1 && empty2)
+ PG_RETURN_INT32(0);
+ else if (empty1)
+ PG_RETURN_INT32(-1);
+ else if (empty2)
+ PG_RETURN_INT32(1);
+
+ if ((cmp = range_cmp_bounds(fcinfo, &lower1, &lower2)) != 0)
+ PG_RETURN_INT32(cmp);
+
+ PG_RETURN_INT32(range_cmp_bounds(fcinfo, &upper1, &upper2));
+ }
+
+ Datum
+ range_lt(PG_FUNCTION_ARGS)
+ {
+ int cmp = range_cmp(fcinfo);
+ PG_RETURN_BOOL(cmp < 0);
+ }
+
+ Datum
+ range_le(PG_FUNCTION_ARGS)
+ {
+ int cmp = range_cmp(fcinfo);
+ PG_RETURN_BOOL(cmp <= 0);
+ }
+
+ Datum
+ range_ge(PG_FUNCTION_ARGS)
+ {
+ int cmp = range_cmp(fcinfo);
+ PG_RETURN_BOOL(cmp >= 0);
+ }
+
+ Datum
+ range_gt(PG_FUNCTION_ARGS)
+ {
+ int cmp = range_cmp(fcinfo);
+ PG_RETURN_BOOL(cmp > 0);
+ }
+
+ /* Hash support */
+ Datum
+ hash_range(PG_FUNCTION_ARGS)
+ {
+ RangeType *r = PG_GETARG_RANGE(0);
+ RangeBound lower;
+ RangeBound upper;
+ bool empty;
+ char flags = 0;
+ uint32 lower_hash = 0;
+ uint32 upper_hash = 0;
+ uint32 result = 0;
+
+ RangeTypeInfo rngtypinfo;
+
+ TypeCacheEntry *typentry;
+ Oid subtype;
+ FunctionCallInfoData locfcinfo;
+
+
+ range_deserialize(fcinfo, r, &lower, &upper, &empty);
+
+ if (lower.rngtypid != upper.rngtypid)
+ elog(ERROR, "range types do not match");
+
+ if (empty)
+ flags |= RANGE_EMPTY;
+
+ flags |= (lower.inclusive) ? RANGE_LB_INC : 0;
+ flags |= (lower.infinite) ? RANGE_LB_INF : 0;
+ flags |= (upper.inclusive) ? RANGE_UB_INC : 0;
+ flags |= (upper.infinite) ? RANGE_UB_INF : 0;
+
+ range_gettypinfo(fcinfo, lower.rngtypid, &rngtypinfo);
+ subtype = rngtypinfo.subtype;
+
+ /*
+ * We arrange to look up the hash function only once per series of
+ * calls, assuming the subtype doesn't change underneath us. The
+ * typcache is used so that we have no memory leakage when being
+ * used as an index support function.
+ */
+ typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
+ if (typentry == NULL || typentry->type_id != subtype)
+ {
+ typentry = lookup_type_cache(subtype, TYPECACHE_HASH_PROC_FINFO);
+ if (!OidIsValid(typentry->hash_proc_finfo.fn_oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("could not identify a hash function for type %s",
+ format_type_be(subtype))));
+ fcinfo->flinfo->fn_extra = (void *) typentry;
+ }
+
+ /*
+ * Apply the hash function to each bound (the hash function shouldn't
+ * care about the collation).
+ */
+ InitFunctionCallInfoData(locfcinfo, &typentry->hash_proc_finfo, 1,
+ InvalidOid, NULL, NULL);
+
+ if (RANGE_HAS_LBOUND(flags))
+ {
+ locfcinfo.arg[0] = lower.val;
+ locfcinfo.argnull[0] = false;
+ locfcinfo.isnull = false;
+ lower_hash = DatumGetUInt32(FunctionCallInvoke(&locfcinfo));
+ }
+ if (RANGE_HAS_UBOUND(flags))
+ {
+ locfcinfo.arg[0] = upper.val;
+ locfcinfo.argnull[0] = false;
+ locfcinfo.isnull = false;
+ upper_hash = DatumGetUInt32(FunctionCallInvoke(&locfcinfo));
+ }
+
+ result = hash_uint32((uint32) flags);
+ result ^= lower_hash;
+ result = (result << 1) | (result >> 31);
+ result ^= upper_hash;
+
+ PG_RETURN_INT32(result);
+ }
+
+ /*
+ *----------------------------------------------------------
+ * CANONICAL FUNCTIONS
+ *
+ * Functions for specific built-in range types.
+ *----------------------------------------------------------
+ */
+
+ Datum
+ int4range_canonical(PG_FUNCTION_ARGS)
+ {
+ RangeType *r = PG_GETARG_RANGE(0);
+
+ RangeBound lower;
+ RangeBound upper;
+ bool empty;
+
+ range_deserialize(fcinfo, r, &lower, &upper, &empty);
+
+ if (empty)
+ PG_RETURN_RANGE(r);
+
+ if (!lower.infinite && !lower.inclusive)
+ {
+ lower.val = DirectFunctionCall2(int4pl, lower.val, Int32GetDatum(1));
+ lower.inclusive = true;
+ }
+
+ if (!upper.infinite && upper.inclusive)
+ {
+ upper.val = DirectFunctionCall2(int4pl, upper.val, Int32GetDatum(1));
+ upper.inclusive = false;
+ }
+
+ PG_RETURN_RANGE(range_serialize(fcinfo, &lower, &upper, false));
+ }
+
+ Datum
+ int8range_canonical(PG_FUNCTION_ARGS)
+ {
+ RangeType *r = PG_GETARG_RANGE(0);
+
+ RangeBound lower;
+ RangeBound upper;
+ bool empty;
+
+ range_deserialize(fcinfo, r, &lower, &upper, &empty);
+
+ if (empty)
+ PG_RETURN_RANGE(r);
+
+ if (!lower.infinite && !lower.inclusive)
+ {
+ lower.val = DirectFunctionCall2(int8pl, lower.val, Int64GetDatum(1));
+ lower.inclusive = true;
+ }
+
+ if (!upper.infinite && upper.inclusive)
+ {
+ upper.val = DirectFunctionCall2(int8pl, upper.val, Int64GetDatum(1));
+ upper.inclusive = false;
+ }
+
+ PG_RETURN_RANGE(range_serialize(fcinfo, &lower, &upper, false));
+ }
+
+ Datum
+ daterange_canonical(PG_FUNCTION_ARGS)
+ {
+ RangeType *r = PG_GETARG_RANGE(0);
+
+ RangeBound lower;
+ RangeBound upper;
+ bool empty;
+
+ range_deserialize(fcinfo, r, &lower, &upper, &empty);
+
+ if (empty)
+ PG_RETURN_RANGE(r);
+
+ if (!lower.infinite && !lower.inclusive)
+ {
+ lower.val = DirectFunctionCall2(date_pli, lower.val, Int32GetDatum(1));
+ lower.inclusive = true;
+ }
+
+ if (!upper.infinite && upper.inclusive)
+ {
+ upper.val = DirectFunctionCall2(date_pli, upper.val, Int32GetDatum(1));
+ upper.inclusive = false;
+ }
+
+ PG_RETURN_RANGE(range_serialize(fcinfo, &lower, &upper, false));
+ }
+
+ /*
+ *----------------------------------------------------------
+ * SUPPORT FUNCTIONS
+ *
+ * These functions aren't in pg_proc, but are useful if
+ * defining new generic range functions in C.
+ *----------------------------------------------------------
+ */
+
+ /*
+ * Serialized format is:
+ *
+ * 4 bytes: Range type Oid
+ * Lower boundary, if any, aligned according to subtype's typalign
+ * Upper boundary, if any, aligned according to subtype's typalign
+ * 1 byte for flags
+ *
+ * This representation is chosen to be compact when the boundary
+ * values need to be MAXALIGNed. A palloc chunk always starts out
+ * MAXALIGNed, and the first 4 bytes will be the length header (range
+ * types are always variable-length), then the next 4 bytes will be
+ * the range type Oid. That leaves the first boundary item MAXALIGNed
+ * without the need for padding.
+ *
+ * However, it requires a slightly odd deserialization strategy,
+ * because we have to read the flags byte before we know whether to
+ * read a boundary value.
+ */
+
+ /*
+ * This serializes a range, but does not canonicalize it. This should
+ * only be called by a canonicalization function.
+ */
+ Datum
+ range_serialize(FunctionCallInfo fcinfo, RangeBound *lower, RangeBound *upper,
+ bool empty)
+ {
+ Datum range;
+ size_t msize;
+ Pointer ptr;
+ int16 typlen;
+ char typalign;
+ bool typbyval;
+ char typstorage;
+ char flags = 0;
+
+ RangeTypeInfo rngtypinfo;
+
+ if (lower->rngtypid != upper->rngtypid)
+ elog(ERROR, "range types do not match");
+
+ range_gettypinfo(fcinfo, lower->rngtypid, &rngtypinfo);
+
+ typlen = rngtypinfo.subtyplen;
+ typalign = rngtypinfo.subtypalign;
+ typbyval = rngtypinfo.subtypbyval;
+ typstorage = rngtypinfo.subtypstorage;
+
+ if (empty)
+ flags |= RANGE_EMPTY;
+ else if (range_cmp_bounds(fcinfo, lower, upper) > 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATA_EXCEPTION),
+ errmsg("range lower bound must be less than or equal to range upper bound")));
+
+ flags |= (lower->inclusive) ? RANGE_LB_INC : 0;
+ flags |= (lower->infinite) ? RANGE_LB_INF : 0;
+ flags |= (upper->inclusive) ? RANGE_UB_INC : 0;
+ flags |= (upper->infinite) ? RANGE_UB_INF : 0;
+
+ msize = VARHDRSZ;
+ msize += sizeof(Oid);
+
+ if (RANGE_HAS_LBOUND(flags))
+ {
+ msize = datum_compute_size(msize, lower->val, typbyval, typalign,
+ typlen, typstorage);
+ }
+
+ if (RANGE_HAS_UBOUND(flags))
+ {
+ msize = datum_compute_size(msize, upper->val, typbyval, typalign,
+ typlen, typstorage);
+ }
+
+ msize += sizeof(char);
+
+ ptr = palloc0(msize);
+ range = (Datum) ptr;
+
+ ptr += VARHDRSZ;
+
+ memcpy(ptr, &lower->rngtypid, sizeof(Oid));
+ ptr += sizeof(Oid);
+
+ if (RANGE_HAS_LBOUND(flags))
+ {
+ Assert(lower->lower);
+ ptr = datum_write(ptr, lower->val, typbyval, typalign, typlen,
+ typstorage);
+ }
+
+ if (RANGE_HAS_UBOUND(flags))
+ {
+ Assert(!upper->lower);
+ ptr = datum_write(ptr, upper->val, typbyval, typalign, typlen,
+ typstorage);
+ }
+
+ memcpy(ptr, &flags, sizeof(char));
+ ptr += sizeof(char);
+
+ SET_VARSIZE(range, msize);
+ PG_RETURN_RANGE(range);
+ }
+
+ void
+ range_deserialize(FunctionCallInfo fcinfo, RangeType *range, RangeBound *lower,
+ RangeBound *upper, bool *empty)
+ {
+ Pointer ptr = VARDATA(range);
+ char typalign;
+ int16 typlen;
+ int16 typbyval;
+ char flags;
+ Oid rngtypid;
+ Datum lbound;
+ Datum ubound;
+ Pointer flags_ptr;
+
+ RangeTypeInfo rngtypinfo;
+
+ memset(lower, 0, sizeof(RangeBound));
+ memset(upper, 0, sizeof(RangeBound));
+
+ /* peek at last byte to read the flag byte */
+ flags_ptr = ptr + VARSIZE(range) - VARHDRSZ - 1;
+ memcpy(&flags, flags_ptr, sizeof(char));
+
+ memcpy(&rngtypid, ptr, sizeof(Oid));
+ ptr += sizeof(Oid);
+
+ if (rngtypid == ANYRANGEOID)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot output a value of type anyrange")));
+
+ range_gettypinfo(fcinfo, rngtypid, &rngtypinfo);
+
+ typalign = rngtypinfo.subtypalign;
+ typlen = rngtypinfo.subtyplen;
+ typbyval = rngtypinfo.subtypbyval;
+
+ if (RANGE_HAS_LBOUND(flags))
+ {
+ ptr = (Pointer) att_align_pointer(ptr, typalign, typlen, ptr);
+ lbound = fetch_att(ptr, typbyval, typlen);
+ ptr = (Pointer) att_addlength_datum(ptr, typlen, PointerGetDatum(ptr));
+ if (typlen == -1)
+ lbound = PointerGetDatum(PG_DETOAST_DATUM(lbound));
+ }
+ else
+ lbound = (Datum) 0;
+
+ if (RANGE_HAS_UBOUND(flags))
+ {
+ ptr = (Pointer) att_align_pointer(ptr, typalign, typlen, ptr);
+ ubound = fetch_att(ptr, typbyval, typlen);
+ ptr = (Pointer) att_addlength_datum(ptr, typlen, PointerGetDatum(ptr));
+ if (typlen == -1)
+ ubound = PointerGetDatum(PG_DETOAST_DATUM(ubound));
+ }
+ else
+ ubound = (Datum) 0;
+
+ *empty = flags & RANGE_EMPTY;
+
+ lower->rngtypid = rngtypid;
+ lower->val = lbound;
+ lower->inclusive = flags & RANGE_LB_INC;
+ lower->infinite = flags & RANGE_LB_INF;
+ lower->lower = true;
+
+ upper->rngtypid = rngtypid;
+ upper->val = ubound;
+ upper->inclusive = flags & RANGE_UB_INC;
+ upper->infinite = flags & RANGE_UB_INF;
+ upper->lower = false;
+ }
+
+ /*
+ * This both serializes and caonicalizes (if applicable) the
+ * range. This should be used by most callers.
+ */
+ Datum
+ make_range(FunctionCallInfo fcinfo, RangeBound *lower, RangeBound *upper,
+ bool empty)
+ {
+ Datum range;
+
+ RangeTypeInfo rngtypinfo;
+
+ range_gettypinfo(fcinfo, lower->rngtypid, &rngtypinfo);
+
+ if (lower->rngtypid != upper->rngtypid)
+ elog(ERROR, "range types do not match");
+
+ range = range_serialize(fcinfo, lower, upper, empty);
+
+ if (rngtypinfo.canonicalFn.fn_addr != NULL)
+ range = FunctionCall1(&rngtypinfo.canonicalFn, range);
+
+ PG_RETURN_RANGE(range);
+ }
+
+ int
+ range_cmp_bounds(FunctionCallInfo fcinfo, RangeBound *b1, RangeBound *b2)
+ {
+ int result;
+
+ RangeTypeInfo rngtypinfo;
+
+ if (b1->infinite && b2->infinite)
+ {
+ if (b1->lower == b2->lower)
+ return 0;
+ else
+ return (b1->lower) ? -1 : 1;
+ }
+ else if (b1->infinite && !b2->infinite)
+ return (b1->lower) ? -1 : 1;
+ else if (!b1->infinite && b2->infinite)
+ return (b2->lower) ? 1 : -1;
+
+ range_gettypinfo(fcinfo, b1->rngtypid, &rngtypinfo);
+ result = DatumGetInt32(FunctionCall2Coll(&rngtypinfo.cmpFn,
+ rngtypinfo.collation,
+ b1->val, b2->val));
+
+ if (result == 0)
+ {
+ if (b1->inclusive && !b2->inclusive)
+ return (b2->lower) ? -1 : 1;
+ else if (!b1->inclusive && b2->inclusive)
+ return (b1->lower) ? 1 : -1;
+ }
+
+ return result;
+ }
+
+ RangeType *
+ make_empty_range(FunctionCallInfo fcinfo, Oid rngtypid)
+ {
+ RangeBound lower;
+ RangeBound upper;
+
+ memset(&lower, 0, sizeof(RangeBound));
+ memset(&upper, 0, sizeof(RangeBound));
+
+ lower.rngtypid = rngtypid;
+ lower.lower = true;
+ upper.rngtypid = rngtypid;
+ upper.lower = false;
+
+ return DatumGetRangeType(make_range(fcinfo, &lower, &upper, true));
+ }
+
+ /*
+ * Fills in rngtypinfo, from a cached copy if available.
+ */
+ void
+ range_gettypinfo(FunctionCallInfo fcinfo, Oid rngtypid,
+ RangeTypeInfo *rngtypinfo)
+ {
+ RangeTypeInfo *cached = (RangeTypeInfo *) fcinfo->flinfo->fn_extra;
+
+ if (cached == NULL)
+ {
+ fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+ sizeof(RangeTypeInfo));
+ cached = (RangeTypeInfo *) fcinfo->flinfo->fn_extra;
+ cached->rngtypid = ~rngtypid;
+ }
+
+ if (cached->rngtypid != rngtypid)
+ {
+ Form_pg_range pg_range;
+ Form_pg_opclass pg_opclass;
+ Form_pg_type pg_type;
+ HeapTuple tup;
+
+ Oid subtypeOid;
+ Oid collationOid;
+ Oid canonicalOid;
+ Oid subfloatOid;
+ Oid opclassOid;
+ Oid cmpFnOid;
+ Oid opfamilyOid;
+ Oid opcintype;
+ int16 subtyplen;
+ char subtypalign;
+ char subtypstorage;
+ bool subtypbyval;
+
+ /* get information from pg_range */
+ tup = SearchSysCache1(RANGETYPE, ObjectIdGetDatum(rngtypid));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for range type %u", rngtypid);
+
+ pg_range = (Form_pg_range) GETSTRUCT(tup);
+
+ subtypeOid = pg_range->rngsubtype;
+ collationOid = pg_range->rngcollation;
+ canonicalOid = pg_range->rngcanonical;
+ opclassOid = pg_range->rngsubopc;
+ subfloatOid = pg_range->rngsubfloat;
+
+ ReleaseSysCache(tup);
+
+ /* get information from pg_opclass */
+ tup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassOid));
+ if (!HeapTupleIsValid(tup))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("operator class with OID %u does not exist",
+ opclassOid)));
+
+ pg_opclass = (Form_pg_opclass) GETSTRUCT(tup);
+
+ opfamilyOid = pg_opclass->opcfamily;
+ opcintype = pg_opclass->opcintype;
+
+ ReleaseSysCache(tup);
+
+ cmpFnOid = get_opfamily_proc(opfamilyOid, opcintype, opcintype,
+ BTORDER_PROC);
+
+ if (!OidIsValid(cmpFnOid))
+ elog(ERROR, "unable to find compare function for operator class %d",
+ opclassOid);
+
+ /* get information from pg_type */
+ tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(subtypeOid));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for type %u", subtypeOid);
+
+ pg_type = (Form_pg_type) GETSTRUCT(tup);
+
+ subtyplen = pg_type->typlen;
+ subtypalign = pg_type->typalign;
+ subtypstorage = pg_type->typstorage;
+ subtypbyval = pg_type->typbyval;
+
+ ReleaseSysCache(tup);
+
+ /* set up the cache */
+
+ if (OidIsValid(canonicalOid))
+ fmgr_info(canonicalOid, &cached->canonicalFn);
+ else
+ cached->canonicalFn.fn_addr = NULL;
+
+ if (OidIsValid(subfloatOid))
+ fmgr_info(subfloatOid, &cached->subfloatFn);
+ else
+ cached->subfloatFn.fn_addr = NULL;
+
+ fmgr_info(cmpFnOid, &cached->cmpFn);
+ cached->subtype = subtypeOid;
+ cached->collation = collationOid;
+ cached->subtyplen = subtyplen;
+ cached->subtypalign = subtypalign;
+ cached->subtypstorage = subtypstorage;
+ cached->subtypbyval = subtypbyval;
+ cached->rngtypid = rngtypid;
+ }
+
+ memcpy(rngtypinfo, cached, sizeof(RangeTypeInfo));
+ }
+
+ /*
+ * Given a string representing the flags for the range type, return the flags
+ * represented as a char.
+ *
+ * Exported so that it can be called by DefineRange to check the default flags.
+ */
+ char
+ range_parse_flags(char *flags_str)
+ {
+ char flags = 0;
+
+ if (flags_str[0] == '\0' ||
+ flags_str[1] == '\0' ||
+ flags_str[2] != '\0')
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("invalid range bound flags"),
+ errhint("Valid values are '[]', '[)', '(]', and '()'.")));
+
+ switch (flags_str[0])
+ {
+ case '[':
+ flags |= RANGE_LB_INC;
+ break;
+ case '(':
+ break;
+ default:
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("invalid range bound flags"),
+ errhint("Valid values are '[]', '[)', '(]', and '()'.")));
+ }
+
+ switch (flags_str[1])
+ {
+ case ']':
+ flags |= RANGE_UB_INC;
+ break;
+ case ')':
+ break;
+ default:
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("invalid range bound flags"),
+ errhint("Valid values are '[]', '[)', '(]', and '()'.")));
+ }
+
+ return flags;
+ }
+
+ /*
+ *----------------------------------------------------------
+ * STATIC FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+ /*
+ * Parse range input, modeled after record_in in rowtypes.c.
+ *
+ * := EMPTY
+ * | ,
+ * := '[' | '('
+ * := ']' | ')'
+ *
+ * Whitespace before or after is ignored. Whitespace within a
+ * is taken literally and becomes the input string for that bound.
+ *
+ * A of length zero is taken as "infinite" (i.e. no bound); unless it
+ * is surrounded by double-quotes, in which case it is the literal empty
+ * string.
+ *
+ * Within a , special characters (such as comma, parenthesis, or
+ * brackets) can be enclosed in double-quotes or escaped with backslash. Within
+ * double-quotes, a double-quote can be escaped with double-quote or backslash.
+ */
+ static void
+ range_parse(char *string, char *flags, char **lbound_str,
+ char **ubound_str)
+ {
+ char *ptr = string;
+ bool infinite;
+
+ *flags = 0;
+
+ /* consume whitespace */
+ while (*ptr != '\0' && isspace(*ptr))
+ ptr++;
+
+ /* check for empty range */
+ if (pg_strncasecmp(ptr, RANGE_EMPTY_LITERAL,
+ strlen(RANGE_EMPTY_LITERAL)) == 0)
+ {
+ *flags = RANGE_EMPTY;
+
+ ptr += strlen(RANGE_EMPTY_LITERAL);
+
+ /* the rest should be whitespace */
+ while (*ptr != '\0' && isspace(*ptr))
+ ptr++;
+
+ /* should have consumed everything */
+ if (*ptr != '\0')
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed range literal: \"%s\"",
+ string),
+ errdetail("Unexpected end of input.")));
+
+ return;
+ }
+
+ if (*ptr == '[' || *ptr == '(')
+ {
+ if (*ptr == '[')
+ *flags |= RANGE_LB_INC;
+ ptr++;
+ }
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed range literal: \"%s\"",
+ string),
+ errdetail("Missing left parenthesis or bracket.")));
+ }
+
+ ptr = range_parse_bound(string, ptr, lbound_str, &infinite);
+ if (infinite)
+ {
+ *flags |= RANGE_LB_INF;
+ *flags &= ~RANGE_LB_INC;
+ }
+
+ if (*ptr != ',')
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed range literal: \"%s\"",
+ string),
+ errdetail("Missing upper bound.")));
+ ptr++;
+
+ ptr = range_parse_bound(string, ptr, ubound_str, &infinite);
+
+ if (*ptr == ')' || *ptr == ']')
+ {
+ if (*ptr == ']')
+ *flags |= RANGE_UB_INC;
+ }
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed range literal: \"%s\"",
+ string),
+ errdetail("Too many boundaries.")));
+ }
+
+ ptr++;
+
+ if (infinite)
+ {
+ *flags |= RANGE_UB_INF;
+ *flags &= ~RANGE_UB_INC;
+ }
+
+ /* consume whitespace */
+ while (*ptr != '\0' && isspace(*ptr))
+ ptr++;
+
+ if (*ptr != '\0')
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed range literal: \"%s\"",
+ string),
+ errdetail("Junk after right parenthesis or bracket.")));
+
+ return;
+ }
+
+ static char *
+ range_parse_bound(char *string, char *ptr, char **bound_str, bool *infinite)
+ {
+ StringInfoData buf;
+
+ /* Check for null: completely empty input means null */
+ if (*ptr == ',' || *ptr == ')' || *ptr == ']')
+ {
+ *bound_str = NULL;
+ *infinite = true;
+ }
+ else
+ {
+ /* Extract string for this column */
+ bool inquote = false;
+
+ initStringInfo(&buf);
+ while (inquote || !(*ptr == ',' || *ptr == ')' || *ptr == ']'))
+ {
+ char ch = *ptr++;
+
+ if (ch == '\0')
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed range literal: \"%s\"",
+ string),
+ errdetail("Unexpected end of input.")));
+ if (ch == '\\')
+ {
+ if (*ptr == '\0')
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed range literal: \"%s\"",
+ string),
+ errdetail("Unexpected end of input.")));
+ appendStringInfoChar(&buf, *ptr++);
+ }
+ else if (ch == '\"')
+ {
+ if (!inquote)
+ inquote = true;
+ else if (*ptr == '\"')
+ {
+ /* doubled quote within quote sequence */
+ appendStringInfoChar(&buf, *ptr++);
+ }
+ else
+ inquote = false;
+ }
+ else
+ appendStringInfoChar(&buf, ch);
+ }
+
+ *bound_str = buf.data;
+ *infinite = false;
+ }
+
+ return ptr;
+ }
+
+ static char *
+ range_deparse(char flags, char *lbound_str, char *ubound_str)
+ {
+ StringInfoData buf;
+
+ initStringInfo(&buf);
+
+ if (flags & RANGE_EMPTY)
+ return pstrdup(RANGE_EMPTY_LITERAL);
+
+ appendStringInfoString(&buf, (flags & RANGE_LB_INC) ? "[" : "(");
+
+ if (RANGE_HAS_LBOUND(flags))
+ appendStringInfoString(&buf, range_bound_escape(lbound_str));
+
+ appendStringInfoString(&buf, ",");
+
+ if (RANGE_HAS_UBOUND(flags))
+ appendStringInfoString(&buf, range_bound_escape(ubound_str));
+
+ appendStringInfoString(&buf, (flags & RANGE_UB_INC) ? "]" : ")");
+
+ return buf.data;
+ }
+
+ static char *
+ range_bound_escape(char *value)
+ {
+ bool nq;
+ char *tmp;
+ StringInfoData buf;
+
+ initStringInfo(&buf);
+
+ /* Detect whether we need double quotes for this value */
+ nq = (value[0] == '\0'); /* force quotes for empty string */
+ for (tmp = value; *tmp; tmp++)
+ {
+ char ch = *tmp;
+
+ if (ch == '"' || ch == '\\' ||
+ ch == '(' || ch == ')' ||
+ ch == '[' || ch == ']' ||
+ ch == ',' ||
+ isspace((unsigned char) ch))
+ {
+ nq = true;
+ break;
+ }
+ }
+
+ /* And emit the string */
+ if (nq)
+ appendStringInfoChar(&buf, '"');
+ for (tmp = value; *tmp; tmp++)
+ {
+ char ch = *tmp;
+
+ if (ch == '"' || ch == '\\')
+ appendStringInfoChar(&buf, ch);
+ appendStringInfoChar(&buf, ch);
+ }
+ if (nq)
+ appendStringInfoChar(&buf, '"');
+
+ return buf.data;
+ }
+
+ static bool
+ range_contains_internal(FunctionCallInfo fcinfo, RangeType *r1, RangeType *r2)
+ {
+ RangeBound lower1;
+ RangeBound upper1;
+ bool empty1;
+ RangeBound lower2;
+ RangeBound upper2;
+ bool empty2;
+
+ range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
+ range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
+
+ if (lower1.rngtypid != upper1.rngtypid ||
+ lower1.rngtypid != lower2.rngtypid ||
+ lower1.rngtypid != upper2.rngtypid)
+ elog(ERROR, "range types do not match");
+
+ if (empty2)
+ return true;
+ else if (empty1)
+ return false;
+
+ if (range_cmp_bounds(fcinfo, &lower1, &lower2) > 0)
+ return false;
+ if (range_cmp_bounds(fcinfo, &upper1, &upper2) < 0)
+ return false;
+
+ return true;
+ }
+
+ /*
+ * datum_compute_size() and datum_write() are modeled after
+ * heap_compute_data_size() and heap_fill_tuple().
+ */
+
+ static Size
+ datum_compute_size(Size data_length, Datum val, bool typbyval, char typalign,
+ int16 typlen, char typstorage)
+ {
+ if (TYPE_IS_PACKABLE(typlen, typstorage) &&
+ VARATT_CAN_MAKE_SHORT(DatumGetPointer(val)))
+ {
+ /*
+ * we're anticipating converting to a short varlena header, so
+ * adjust length and don't count any alignment
+ */
+ data_length += VARATT_CONVERTED_SHORT_SIZE(DatumGetPointer(val));
+ }
+ else
+ {
+ data_length = att_align_datum(data_length, typalign, typlen, val);
+ data_length = att_addlength_datum(data_length, typlen, val);
+ }
+
+ return data_length;
+ }
+
+ /*
+ * Modified version of the code in heap_fill_tuple(). Writes the datum to ptr
+ * using the correct alignment, and also uses short varlena header if
+ * applicable.
+ */
+ static Pointer
+ datum_write(Pointer ptr, Datum datum, bool typbyval, char typalign,
+ int16 typlen, char typstorage)
+ {
+ Size data_length;
+
+ if (typbyval)
+ {
+ /* pass-by-value */
+ ptr = (char *) att_align_nominal(ptr, typalign);
+ store_att_byval(ptr, datum, typlen);
+ data_length = typlen;
+ }
+ else if (typlen == -1)
+ {
+ /* varlena */
+ Pointer val = DatumGetPointer(datum);
+
+ if (VARATT_IS_EXTERNAL(val))
+ {
+ /* no alignment, since it's short by definition */
+ data_length = VARSIZE_EXTERNAL(val);
+ memcpy(ptr, val, data_length);
+ }
+ else if (VARATT_IS_SHORT(val))
+ {
+ /* no alignment for short varlenas */
+ data_length = VARSIZE_SHORT(val);
+ memcpy(ptr, val, data_length);
+ }
+ else if (TYPE_IS_PACKABLE(typlen, typstorage) &&
+ VARATT_CAN_MAKE_SHORT(val))
+ {
+ /* convert to short varlena -- no alignment */
+ data_length = VARATT_CONVERTED_SHORT_SIZE(val);
+ SET_VARSIZE_SHORT(ptr, data_length);
+ memcpy(ptr + 1, VARDATA(val), data_length - 1);
+ }
+ else
+ {
+ /* full 4-byte header varlena */
+ ptr = (char *) att_align_nominal(ptr, typalign);
+ data_length = VARSIZE(val);
+ memcpy(ptr, val, data_length);
+ }
+ }
+ else if (typlen == -2)
+ {
+ /* cstring ... never needs alignment */
+ Assert(typalign == 'c');
+ data_length = strlen(DatumGetCString(datum)) + 1;
+ memcpy(ptr, DatumGetPointer(datum), data_length);
+ }
+ else
+ {
+ /* fixed-length pass-by-reference */
+ ptr = (char *) att_align_nominal(ptr, typalign);
+ Assert(typlen > 0);
+ data_length = typlen;
+ memcpy(ptr, DatumGetPointer(datum), data_length);
+ }
+
+ ptr += data_length;
+
+ return ptr;
+ }
*** /dev/null
--- b/src/backend/utils/adt/rangetypes_gist.c
***************
*** 0 ****
--- 1,554 ----
+ /*-------------------------------------------------------------------------
+ *
+ * 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/fmgroids.h"
+ #include "utils/lsyscache.h"
+ #include "utils/rangetypes.h"
+
+ #define RANGESTRAT_EQ 1
+ #define RANGESTRAT_NE 2
+ #define RANGESTRAT_OVERLAPS 3
+ #define RANGESTRAT_CONTAINS_ELEM 4
+ #define RANGESTRAT_ELEM_CONTAINED_BY 5
+ #define RANGESTRAT_CONTAINS 6
+ #define RANGESTRAT_CONTAINED_BY 7
+ #define RANGESTRAT_BEFORE 8
+ #define RANGESTRAT_AFTER 9
+ #define RANGESTRAT_OVERLEFT 10
+ #define RANGESTRAT_OVERRIGHT 11
+ #define RANGESTRAT_ADJACENT 12
+
+ static RangeType *range_super_union(FunctionCallInfo fcinfo, RangeType *r1,
+ RangeType *r2);
+ static bool range_gist_consistent_int(FunctionCallInfo fcinfo,
+ StrategyNumber strategy, RangeType *key,
+ RangeType *query);
+ static bool range_gist_consistent_leaf(FunctionCallInfo fcinfo,
+ StrategyNumber strategy, RangeType *key,
+ RangeType *query);
+ static int sort_item_cmp(const void *a, const void *b);
+
+ /*
+ * Auxiliary structure for picksplit method.
+ */
+ typedef struct
+ {
+ int index;
+ RangeType *data;
+ FunctionCallInfo fcinfo;
+ } PickSplitSortItem;
+
+
+ /* 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(fcinfo, 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(fcinfo, &lower, &upper, false));
+ break;
+ default:
+ query = DatumGetRangeType(dquery);
+ break;
+ }
+
+ if(GIST_LEAF(entry))
+ PG_RETURN_BOOL(range_gist_consistent_leaf(
+ fcinfo, strategy, key, query));
+ else
+ PG_RETURN_BOOL(range_gist_consistent_int(
+ fcinfo, strategy, key, query));
+ }
+
+ Datum
+ range_gist_union(PG_FUNCTION_ARGS)
+ {
+ GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
+ GISTENTRY *ent = entryvec->vector;
+ RangeType *result_range;
+ int i;
+
+ result_range = DatumGetRangeType(ent[0].key);
+
+ for (i = 1; i < entryvec->n; i++)
+ {
+ result_range = range_super_union(fcinfo, result_range,
+ DatumGetRangeType(ent[i].key));
+ }
+
+ PG_RETURN_RANGE(result_range);
+ }
+
+ Datum
+ range_gist_compress(PG_FUNCTION_ARGS)
+ {
+ GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+ PG_RETURN_POINTER(entry);
+ }
+
+ Datum
+ range_gist_decompress(PG_FUNCTION_ARGS)
+ {
+ GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+ PG_RETURN_POINTER(entry);
+ }
+
+ Datum
+ range_gist_penalty(PG_FUNCTION_ARGS)
+ {
+ GISTENTRY *origentry = (GISTENTRY *) PG_GETARG_POINTER(0);
+ GISTENTRY *newentry = (GISTENTRY *) PG_GETARG_POINTER(1);
+ float *penalty = (float *) PG_GETARG_POINTER(2);
+ RangeType *orig = DatumGetRangeType(origentry->key);
+ RangeType *new = DatumGetRangeType(newentry->key);
+ RangeType *s_union = range_super_union(fcinfo, orig, new);
+
+ FmgrInfo *subtype_float;
+
+ RangeBound lower1, lower2;
+ RangeBound upper1, upper2;
+ bool empty1, empty2;
+ float length1, length2;
+
+ RangeTypeInfo rngtypinfo;
+
+ range_deserialize(fcinfo, orig, &lower1, &upper1, &empty1);
+ range_deserialize(fcinfo, s_union, &lower2, &upper2, &empty2);
+
+ range_gettypinfo(fcinfo, lower1.rngtypid, &rngtypinfo);
+ subtype_float = &rngtypinfo.subfloatFn;
+
+ if (empty1)
+ length1 = 0.0;
+ else if (lower1.infinite || upper1.infinite)
+ length1 = 1.0/0.0;
+ else if (subtype_float->fn_addr != NULL)
+ {
+ double l = DatumGetFloat8(FunctionCall1(subtype_float, lower1.val));
+ double u = DatumGetFloat8(FunctionCall1(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 (subtype_float->fn_addr != NULL)
+ {
+ double l = DatumGetFloat8(FunctionCall1(subtype_float, lower2.val));
+ double u = DatumGetFloat8(FunctionCall1(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 sorted using
+ * sort_item_cmp function. After that first half of periods goes to the left
+ * datum, and the second half of periods goes to the right datum.
+ */
+ Datum
+ range_gist_picksplit(PG_FUNCTION_ARGS)
+ {
+ GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
+ GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+ OffsetNumber i;
+ RangeType *pred_left;
+ RangeType *pred_right;
+ PickSplitSortItem *sortItems;
+ int nbytes;
+ OffsetNumber split_idx;
+ OffsetNumber *left;
+ OffsetNumber *right;
+ OffsetNumber maxoff;
+
+ maxoff = entryvec->n - 1;
+ nbytes = (maxoff + 1) * sizeof(OffsetNumber);
+ sortItems = (PickSplitSortItem *) palloc(
+ maxoff * sizeof(PickSplitSortItem));
+ v->spl_left = (OffsetNumber *) palloc(nbytes);
+ v->spl_right = (OffsetNumber *) palloc(nbytes);
+
+ /*
+ * Preparing auxiliary array and sorting.
+ */
+ for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
+ {
+ sortItems[i - 1].index = i;
+ sortItems[i - 1].data = DatumGetRangeType(entryvec->vector[i].key);
+ sortItems[i - 1].fcinfo = fcinfo;
+ }
+ qsort(sortItems, maxoff, sizeof(PickSplitSortItem), sort_item_cmp);
+ split_idx = maxoff / 2;
+
+ left = v->spl_left;
+ v->spl_nleft = 0;
+ right = v->spl_right;
+ v->spl_nright = 0;
+
+ /*
+ * First half of segs goes to the left datum.
+ */
+ pred_left = DatumGetRangeType(sortItems[0].data);
+ *left++ = sortItems[0].index;
+ v->spl_nleft++;
+ for (i = 1; i < split_idx; i++)
+ {
+ pred_left = range_super_union(fcinfo, pred_left,
+ DatumGetRangeType(sortItems[i].data));
+ *left++ = sortItems[i].index;
+ v->spl_nleft++;
+ }
+
+ /*
+ * Second half of segs goes to the right datum.
+ */
+ pred_right = DatumGetRangeType(sortItems[split_idx].data);
+ *right++ = sortItems[split_idx].index;
+ v->spl_nright++;
+ for (i = split_idx + 1; i < maxoff; i++)
+ {
+ pred_right = range_super_union(fcinfo, pred_right,
+ DatumGetRangeType(sortItems[i].data));
+ *right++ = sortItems[i].index;
+ v->spl_nright++;
+ }
+
+ *left = *right = FirstOffsetNumber; /* sentinel value, see dosplit() */
+
+ v->spl_ldatum = RangeTypeGetDatum(pred_left);
+ v->spl_rdatum = RangeTypeGetDatum(pred_right);
+
+ PG_RETURN_POINTER(v);
+ }
+
+ Datum
+ range_gist_same(PG_FUNCTION_ARGS)
+ {
+ Datum r1 = PG_GETARG_DATUM(0);
+ Datum r2 = PG_GETARG_DATUM(1);
+ bool *result = (bool *) PG_GETARG_POINTER(2);
+
+ *result = DatumGetBool(OidFunctionCall2(F_RANGE_EQ, r1, r2));
+ PG_RETURN_POINTER(result);
+ }
+
+ /*
+ *----------------------------------------------------------
+ * STATIC FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+ /* return the smallest range that contains r1 and r2 */
+ static RangeType *
+ range_super_union(FunctionCallInfo fcinfo, RangeType *r1, RangeType *r2)
+ {
+ RangeBound lower1, lower2;
+ RangeBound upper1, upper2;
+ bool empty1, empty2;
+ RangeBound *result_lower;
+ RangeBound *result_upper;
+
+ range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
+ range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
+
+ if (empty1)
+ return r2;
+ if (empty2)
+ return r1;
+
+ if (range_cmp_bounds(fcinfo, &lower1, &lower2) <= 0)
+ result_lower = &lower1;
+ else
+ result_lower = &lower2;
+
+ if (range_cmp_bounds(fcinfo, &upper1, &upper2) >= 0)
+ result_upper = &upper1;
+ else
+ result_upper = &upper2;
+
+ /* optimization to avoid constructing a new range */
+ if (result_lower == &lower1 && result_upper == &upper1)
+ return r1;
+ if (result_lower == &lower2 && result_upper == &upper2)
+ return r2;
+
+ return DatumGetRangeType(
+ make_range(fcinfo, result_lower, result_upper, false));
+ }
+
+ static bool
+ range_gist_consistent_int(FunctionCallInfo fcinfo, StrategyNumber strategy,
+ RangeType *key, RangeType *query)
+ {
+ Oid proc = InvalidOid;
+
+ RangeBound lower1, lower2;
+ RangeBound upper1, upper2;
+ bool empty1, empty2;
+
+ bool retval;
+ bool negate = false;
+
+ range_deserialize(fcinfo, key, &lower1, &upper1, &empty1);
+ range_deserialize(fcinfo, query, &lower2, &upper2, &empty2);
+
+ switch (strategy)
+ {
+ case RANGESTRAT_EQ:
+ proc = F_RANGE_CONTAINS;
+ break;
+ case RANGESTRAT_NE:
+ return true;
+ break;
+ case RANGESTRAT_OVERLAPS:
+ proc = F_RANGE_OVERLAPS;
+ break;
+ case RANGESTRAT_CONTAINS_ELEM:
+ case RANGESTRAT_CONTAINS:
+ proc = F_RANGE_CONTAINS;
+ break;
+ case RANGESTRAT_ELEM_CONTAINED_BY:
+ case RANGESTRAT_CONTAINED_BY:
+ return true;
+ break;
+ case RANGESTRAT_BEFORE:
+ if (empty1)
+ return false;
+ proc = F_RANGE_OVERRIGHT;
+ negate = true;
+ break;
+ case RANGESTRAT_AFTER:
+ if (empty1)
+ return false;
+ proc = F_RANGE_OVERLEFT;
+ negate = true;
+ break;
+ case RANGESTRAT_OVERLEFT:
+ if (empty1)
+ return false;
+ proc = F_RANGE_AFTER;
+ negate = true;
+ break;
+ case RANGESTRAT_OVERRIGHT:
+ if (empty1)
+ return false;
+ proc = F_RANGE_BEFORE;
+ negate = true;
+ break;
+ case RANGESTRAT_ADJACENT:
+ if (empty1 || empty2)
+ return false;
+ if (DatumGetBool(
+ OidFunctionCall2(F_RANGE_ADJACENT,
+ RangeTypeGetDatum(key),
+ RangeTypeGetDatum(query))))
+ return true;
+ proc = F_RANGE_OVERLAPS;
+ break;
+ }
+
+ retval = DatumGetBool(OidFunctionCall2(proc, RangeTypeGetDatum(key),
+ RangeTypeGetDatum(query)));
+
+ if (negate)
+ retval = !retval;
+
+ PG_RETURN_BOOL(retval);
+ }
+
+ static bool
+ range_gist_consistent_leaf(FunctionCallInfo fcinfo, StrategyNumber strategy,
+ RangeType *key, RangeType *query)
+ {
+ Oid proc = InvalidOid;
+
+ RangeBound lower1, lower2;
+ RangeBound upper1, upper2;
+ bool empty1, empty2;
+
+ range_deserialize(fcinfo, key, &lower1, &upper1, &empty1);
+ range_deserialize(fcinfo, query, &lower2, &upper2, &empty2);
+
+ switch (strategy)
+ {
+ case RANGESTRAT_EQ:
+ proc = F_RANGE_EQ;
+ break;
+ case RANGESTRAT_NE:
+ proc = F_RANGE_NE;
+ break;
+ case RANGESTRAT_OVERLAPS:
+ proc = F_RANGE_OVERLAPS;
+ break;
+ case RANGESTRAT_CONTAINS_ELEM:
+ case RANGESTRAT_CONTAINS:
+ proc = F_RANGE_CONTAINS;
+ break;
+ case RANGESTRAT_ELEM_CONTAINED_BY:
+ case RANGESTRAT_CONTAINED_BY:
+ proc = F_RANGE_CONTAINED_BY;
+ break;
+ case RANGESTRAT_BEFORE:
+ if (empty1 || empty2)
+ return false;
+ proc = F_RANGE_BEFORE;
+ break;
+ case RANGESTRAT_AFTER:
+ if (empty1 || empty2)
+ return false;
+ proc = F_RANGE_AFTER;
+ break;
+ case RANGESTRAT_OVERLEFT:
+ if (empty1 || empty2)
+ return false;
+ proc = F_RANGE_OVERLEFT;
+ break;
+ case RANGESTRAT_OVERRIGHT:
+ if (empty1 || empty2)
+ return false;
+ proc = F_RANGE_OVERRIGHT;
+ break;
+ case RANGESTRAT_ADJACENT:
+ if (empty1 || empty2)
+ return false;
+ proc = F_RANGE_ADJACENT;
+ break;
+ }
+
+ return DatumGetBool(OidFunctionCall2(proc, RangeTypeGetDatum(key),
+ RangeTypeGetDatum(query)));
+ }
+
+ /*
+ * Compare function for PickSplitSortItem. This is actually the
+ * interesting part of the picksplit algorithm.
+ *
+ * We want to separate out empty ranges, bounded ranges, and unbounded
+ * ranges. We assume that "contains" and "overlaps" are the most
+ * important queries, so empty ranges will rarely match and unbounded
+ * ranges frequently will. Bounded ranges should be in the middle.
+ *
+ * Empty ranges we push all the way to the left, then bounded ranges
+ * (sorted on lower bound, then upper), then ranges with no lower
+ * bound, then ranges with no upper bound; and finally, ranges with no
+ * upper or lower bound all the way to the right.
+ */
+ static int
+ sort_item_cmp(const void *a, const void *b)
+ {
+ PickSplitSortItem *i1 = (PickSplitSortItem *)a;
+ PickSplitSortItem *i2 = (PickSplitSortItem *)b;
+ RangeType *r1 = i1->data;
+ RangeType *r2 = i2->data;
+
+ RangeBound lower1, lower2;
+ RangeBound upper1, upper2;
+ bool empty1, empty2;
+
+ FunctionCallInfo fcinfo = i1->fcinfo;
+
+ int cmp;
+
+ range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
+ range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
+
+ if (empty1 || empty2)
+ {
+ if (empty1 && empty2)
+ return 0;
+ else if (empty1)
+ return -1;
+ else if (empty2)
+ return 1;
+ else
+ Assert(false);
+ }
+
+ /*
+ * If both lower or both upper bounds are infinite, we sort by
+ * ascending range size. That means that if both upper bounds are
+ * infinite, we sort by the lower bound _descending_. That creates
+ * a slightly odd total order, but keeps the pages with very
+ * unselective predicates grouped more closely together on the
+ * right.
+ */
+ if (lower1.infinite || upper1.infinite ||
+ lower2.infinite || upper2.infinite)
+ {
+ if (lower1.infinite && lower2.infinite)
+ return range_cmp_bounds(fcinfo, &upper1, &upper2);
+ else if (lower1.infinite)
+ return -1;
+ else if (lower2.infinite)
+ return 1;
+ else if (upper1.infinite && upper2.infinite)
+ return -1 * range_cmp_bounds(fcinfo, &lower1, &lower2);
+ else if (upper1.infinite)
+ return 1;
+ else if (upper2.infinite)
+ return -1;
+ else
+ Assert(false);
+ }
+
+ if ((cmp = range_cmp_bounds(fcinfo, &lower1, &lower2)) != 0)
+ return cmp;
+
+ return range_cmp_bounds(fcinfo, &upper1, &upper2);
+ }
*** 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,2961 ----
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_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,571 ----
},
2048
},
+ {RangeRelationId, /* RANGETYPE */
+ RangeTypidIndexId,
+ 1,
+ {
+ Anum_pg_range_rngtypid,
+ 0,
+ 0,
+ 0
+ },
+ 1024
+ },
{RelationRelationId, /* RELNAMENSP */
ClassNameNspIndexId,
2,
*** a/src/backend/utils/fmgr/funcapi.c
--- b/src/backend/utils/fmgr/funcapi.c
***************
*** 407,417 **** resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
int nargs = declared_args->dim1;
bool have_anyelement_result = false;
bool have_anyarray_result = false;
bool have_anynonarray = false;
bool have_anyenum = false;
Oid anyelement_type = InvalidOid;
Oid anyarray_type = InvalidOid;
! Oid anycollation;
int i;
/* See if there are any polymorphic outputs; quick out if not */
--- 407,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 = InvalidOid;
int i;
/* See if there are any polymorphic outputs; quick out if not */
***************
*** 433,443 **** resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
have_anyelement_result = true;
have_anyenum = true;
break;
default:
break;
}
}
! if (!have_anyelement_result && !have_anyarray_result)
return true;
/*
--- 435,449 ----
have_anyelement_result = true;
have_anyenum = true;
break;
+ case ANYRANGEOID:
+ have_anyrange_result = true;
+ break;
default:
break;
}
}
! if (!have_anyelement_result && !have_anyarray_result &&
! !have_anyrange_result)
return true;
/*
***************
*** 461,480 **** resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
if (!OidIsValid(anyarray_type))
anyarray_type = get_call_expr_argtype(call_expr, i);
break;
default:
break;
}
}
/* If nothing found, parser messed up */
! if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type))
return false;
/* If needed, deduce one polymorphic type from the other */
if (have_anyelement_result && !OidIsValid(anyelement_type))
! anyelement_type = resolve_generic_type(ANYELEMENTOID,
! anyarray_type,
! ANYARRAYOID);
if (have_anyarray_result && !OidIsValid(anyarray_type))
anyarray_type = resolve_generic_type(ANYARRAYOID,
anyelement_type,
--- 467,513 ----
if (!OidIsValid(anyarray_type))
anyarray_type = get_call_expr_argtype(call_expr, i);
break;
+ case ANYRANGEOID:
+ if (!OidIsValid(anyrange_type))
+ anyrange_type = get_call_expr_argtype(call_expr, i);
+ break;
default:
break;
}
}
/* If nothing found, parser messed up */
! if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type) &&
! !OidIsValid(anyrange_type))
! return false;
!
! /*
! * We can't deduce a range type from the subtype, because there may be
! * multiple range types for a single subtype.
! */
! if (have_anyrange_result && !OidIsValid(anyrange_type))
return false;
/* If needed, deduce one polymorphic type from the other */
if (have_anyelement_result && !OidIsValid(anyelement_type))
! {
! if (OidIsValid(anyarray_type))
! anyelement_type = resolve_generic_type(ANYELEMENTOID,
! anyarray_type,
! ANYARRAYOID);
! if (OidIsValid(anyrange_type))
! {
! Oid subtype = resolve_generic_type(ANYELEMENTOID,
! anyrange_type,
! ANYRANGEOID);
! if (OidIsValid(anyelement_type) &&
! anyelement_type != subtype)
! return false;
! else
! anyelement_type = subtype;
! }
! }
!
if (have_anyarray_result && !OidIsValid(anyarray_type))
anyarray_type = resolve_generic_type(ANYARRAYOID,
anyelement_type,
***************
*** 492,498 **** resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
* Identify the collation to use for polymorphic OUT parameters. (It'll
* necessarily be the same for both anyelement and anyarray.)
*/
! anycollation = get_typcollation(OidIsValid(anyelement_type) ? anyelement_type : anyarray_type);
if (OidIsValid(anycollation))
{
/*
--- 525,536 ----
* Identify the collation to use for polymorphic OUT parameters. (It'll
* necessarily be the same for both anyelement and anyarray.)
*/
!
! if (OidIsValid(anyelement_type))
! anycollation = get_typcollation(anyelement_type);
! else if (OidIsValid(anyarray_type))
! anycollation = get_typcollation(anyarray_type);
!
if (OidIsValid(anycollation))
{
/*
***************
*** 529,534 **** resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
--- 567,580 ----
0);
TupleDescInitEntryCollation(tupdesc, i + 1, anycollation);
break;
+ case ANYRANGEOID:
+ TupleDescInitEntry(tupdesc, i + 1,
+ NameStr(tupdesc->attrs[i]->attname),
+ anyrange_type,
+ -1,
+ 0);
+ TupleDescInitEntryCollation(tupdesc, i + 1, anycollation);
+ break;
default:
break;
}
***************
*** 552,559 **** resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
--- 598,607 ----
{
bool have_anyelement_result = false;
bool have_anyarray_result = false;
+ bool have_anyrange_result = false;
Oid anyelement_type = InvalidOid;
Oid anyarray_type = InvalidOid;
+ Oid anyrange_type = InvalidOid;
int inargno;
int i;
***************
*** 597,602 **** resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
--- 645,665 ----
argtypes[i] = anyarray_type;
}
break;
+ case ANYRANGEOID:
+ if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
+ have_anyrange_result = true;
+ else
+ {
+ if (!OidIsValid(anyrange_type))
+ {
+ anyrange_type = get_call_expr_argtype(call_expr,
+ inargno);
+ if (!OidIsValid(anyrange_type))
+ return false;
+ }
+ argtypes[i] = anyrange_type;
+ }
+ break;
default:
break;
}
***************
*** 605,622 **** resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
}
/* Done? */
! if (!have_anyelement_result && !have_anyarray_result)
return true;
/* If no input polymorphics, parser messed up */
! if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type))
return false;
/* If needed, deduce one polymorphic type from the other */
if (have_anyelement_result && !OidIsValid(anyelement_type))
! anyelement_type = resolve_generic_type(ANYELEMENTOID,
! anyarray_type,
! ANYARRAYOID);
if (have_anyarray_result && !OidIsValid(anyarray_type))
anyarray_type = resolve_generic_type(ANYARRAYOID,
anyelement_type,
--- 668,709 ----
}
/* Done? */
! if (!have_anyelement_result && !have_anyarray_result &&
! !have_anyrange_result)
return true;
+ /*
+ * We can't deduce a range type from the subtype, because there may be
+ * multiple range types for a single subtype.
+ */
+ if (have_anyrange_result && !OidIsValid(anyrange_type))
+ return false;
+
/* If no input polymorphics, parser messed up */
! if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type) &&
! !OidIsValid(anyrange_type))
return false;
/* If needed, deduce one polymorphic type from the other */
if (have_anyelement_result && !OidIsValid(anyelement_type))
! {
! if (OidIsValid(anyarray_type))
! anyelement_type = resolve_generic_type(ANYELEMENTOID,
! anyarray_type,
! ANYARRAYOID);
! if (OidIsValid(anyrange_type))
! {
! Oid subtype = resolve_generic_type(ANYELEMENTOID,
! anyrange_type,
! ANYRANGEOID);
! if (OidIsValid(anyelement_type) &&
! anyelement_type != subtype)
! return false;
! else
! anyelement_type = subtype;
! }
! }
!
if (have_anyarray_result && !OidIsValid(anyarray_type))
anyarray_type = resolve_generic_type(ANYARRAYOID,
anyelement_type,
***************
*** 637,642 **** resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
--- 724,732 ----
case ANYARRAYOID:
argtypes[i] = anyarray_type;
break;
+ case ANYRANGEOID:
+ argtypes[i] = anyrange_type;
+ break;
default:
break;
}
*** a/src/bin/pg_dump/pg_dump.c
--- b/src/bin/pg_dump/pg_dump.c
***************
*** 52,57 ****
--- 52,58 ----
#include "catalog/pg_largeobject.h"
#include "catalog/pg_largeobject_metadata.h"
#include "catalog/pg_proc.h"
+ #include "catalog/pg_range.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
#include "libpq/libpq-fs.h"
***************
*** 167,172 **** static void dumpExtension(Archive *fout, ExtensionInfo *extinfo);
--- 168,174 ----
static void dumpType(Archive *fout, TypeInfo *tyinfo);
static void dumpBaseType(Archive *fout, TypeInfo *tyinfo);
static void dumpEnumType(Archive *fout, TypeInfo *tyinfo);
+ static void dumpRangeType(Archive *fout, TypeInfo *tyinfo);
static void dumpDomain(Archive *fout, TypeInfo *tyinfo);
static void dumpCompositeType(Archive *fout, TypeInfo *tyinfo);
static void dumpCompositeTypeColComments(Archive *fout, TypeInfo *tyinfo);
***************
*** 2989,2995 **** getTypes(int *numTypes)
* should copy the base type's catId, but then it might capture the
* pg_depend entries for the type, which we don't want.
*/
! if (tyinfo[i].dobj.dump && tyinfo[i].typtype == TYPTYPE_BASE)
{
stinfo = (ShellTypeInfo *) malloc(sizeof(ShellTypeInfo));
stinfo->dobj.objType = DO_SHELL_TYPE;
--- 2991,2998 ----
* should copy the base type's catId, but then it might capture the
* pg_depend entries for the type, which we don't want.
*/
! if (tyinfo[i].dobj.dump && (tyinfo[i].typtype == TYPTYPE_BASE ||
! tyinfo[i].typtype == TYPTYPE_RANGE))
{
stinfo = (ShellTypeInfo *) malloc(sizeof(ShellTypeInfo));
stinfo->dobj.objType = DO_SHELL_TYPE;
***************
*** 3700,3706 **** getFuncs(int *numFuncs)
* so be sure to fetch any such functions.
*/
! if (g_fout->remoteVersion >= 70300)
{
appendPQExpBuffer(query,
"SELECT tableoid, oid, proname, prolang, "
--- 3703,3734 ----
* so be sure to fetch any such functions.
*/
! if (g_fout->remoteVersion >= 90200)
! {
! appendPQExpBuffer(query,
! "SELECT tableoid, oid, proname, prolang, "
! "pronargs, proargtypes, prorettype, proacl, "
! "pronamespace, "
! "(%s proowner) AS rolname "
! "FROM pg_proc p "
! "WHERE NOT proisagg AND "
! " NOT EXISTS (SELECT 1 FROM pg_depend "
! " WHERE classid = 'pg_proc'::regclass AND "
! " objid = p.oid AND deptype = 'i') AND "
! "(pronamespace != "
! "(SELECT oid FROM pg_namespace "
! "WHERE nspname = 'pg_catalog')",
! username_subquery);
! if (binary_upgrade && g_fout->remoteVersion >= 90100)
! appendPQExpBuffer(query,
! " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
! "classid = 'pg_proc'::regclass AND "
! "objid = p.oid AND "
! "refclassid = 'pg_extension'::regclass AND "
! "deptype = 'e')");
! appendPQExpBuffer(query, ")");
! }
! else if (g_fout->remoteVersion >= 70300)
{
appendPQExpBuffer(query,
"SELECT tableoid, oid, proname, prolang, "
***************
*** 7309,7314 **** dumpType(Archive *fout, TypeInfo *tyinfo)
--- 7337,7344 ----
dumpCompositeType(fout, tyinfo);
else if (tyinfo->typtype == TYPTYPE_ENUM)
dumpEnumType(fout, tyinfo);
+ else if (tyinfo->typtype == TYPTYPE_RANGE)
+ dumpRangeType(fout, tyinfo);
}
/*
***************
*** 7433,7438 **** dumpEnumType(Archive *fout, TypeInfo *tyinfo)
--- 7463,7621 ----
}
/*
+ * dumpRangeType
+ * writes out to fout the queries to recreate a user-defined range type
+ */
+ static void
+ dumpRangeType(Archive *fout, TypeInfo *tyinfo)
+ {
+ PQExpBuffer q = createPQExpBuffer();
+ PQExpBuffer delq = createPQExpBuffer();
+ PQExpBuffer labelq = createPQExpBuffer();
+ PQExpBuffer query = createPQExpBuffer();
+ PGresult *res;
+ int num;
+
+ Oid collationOid;
+ Oid opclassOid;
+ Oid analyzeOid;
+ Oid canonicalOid;
+ Oid subfloatOid;
+
+ /* Set proper schema search path */
+ selectSourceSchema("pg_catalog");
+
+ appendPQExpBuffer(query,
+ "SELECT rngtypid, "
+ "format_type(rngsubtype, NULL) as rngsubtype, "
+ "rngsubtype::oid as rngsubtypeoid, "
+ "opc.opcname AS opcname, "
+ "CASE WHEN rngcollation = st.typcollation THEN 0 "
+ " ELSE rngcollation END AS collation, "
+ "CASE WHEN opcdefault THEN 0 ELSE rngsubopc END "
+ " AS rngsubopc, "
+ "(SELECT nspname FROM pg_namespace nsp "
+ " WHERE nsp.oid = opc.opcnamespace) AS opcnsp, "
+ "t.typanalyze, t.typanalyze::oid as typanalyzeoid, "
+ "rngcanonical, rngcanonical::oid as rngcanonicaloid, "
+ "rngsubfloat, rngsubfloat::oid as rngsubfloatoid "
+ "FROM pg_catalog.pg_type t, pg_type st, "
+ " pg_catalog.pg_opclass opc, pg_catalog.pg_range r "
+ "WHERE t.oid = rngtypid AND st.oid = rngsubtype AND "
+ " opc.oid = rngsubopc AND rngtypid = '%u'",
+ tyinfo->dobj.catId.oid);
+
+ res = PQexec(g_conn, query->data);
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
+
+ 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));
+
+ /* We might already have a shell type, but setting pg_type_oid is harmless */
+ if (binary_upgrade)
+ binary_upgrade_set_type_oids_by_type_oid(q, tyinfo->dobj.catId.oid);
+
+ appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (",
+ fmtId(tyinfo->dobj.name));
+
+ /* SUBTYPE */
+ appendPQExpBuffer(q, "\n SUBTYPE = %s",
+ PQgetvalue(res, 0, PQfnumber(res, "rngsubtype")));
+
+ /* COLLATION */
+ collationOid = atooid(PQgetvalue(res, 0,
+ PQfnumber(res, "collation")));
+ if (OidIsValid(collationOid))
+ {
+ CollInfo *coll;
+
+ coll = findCollationByOid(collationOid);
+ if (coll)
+ {
+ /* always schema-qualify, don't try to be smart */
+ appendPQExpBuffer(q, ",\n COLLATION = %s.",
+ fmtId(coll->dobj.namespace->dobj.name));
+ appendPQExpBuffer(q, "%s",
+ fmtId(coll->dobj.name));
+ }
+ }
+
+ /* SUBTYPE_OPCLASS */
+ opclassOid = atooid(PQgetvalue(res, 0,
+ PQfnumber(res, "rngsubopc")));
+ if (OidIsValid(opclassOid))
+ {
+ char *opcname = PQgetvalue(res, 0, PQfnumber(res, "opcname"));
+ char *nspname = PQgetvalue(res, 0, PQfnumber(res, "opcnsp"));
+
+ /* always schema-qualify, don't try to be smart */
+ appendPQExpBuffer(q, ",\n SUBTYPE_OPCLASS = %s.",
+ fmtId(nspname));
+ appendPQExpBuffer(q, "%s", fmtId(opcname));
+ }
+
+ /* ANALYZE */
+ analyzeOid = atooid(PQgetvalue(res, 0,
+ PQfnumber(res, "typanalyzeoid")));
+ if (OidIsValid(analyzeOid))
+ appendPQExpBuffer(q, ",\n ANALYZE = %s",
+ PQgetvalue(res, 0, PQfnumber(res, "typanalyze")));
+
+ /* CANONICAL */
+ canonicalOid = atooid(PQgetvalue(res, 0,
+ PQfnumber(res, "rngcanonicaloid")));
+ if (OidIsValid(canonicalOid))
+ appendPQExpBuffer(q, ",\n CANONICAL = %s",
+ PQgetvalue(res, 0, PQfnumber(res, "rngcanonical")));
+
+ /* SUBTYPE_FLOAT */
+ subfloatOid = atooid(PQgetvalue(res, 0, PQfnumber(res, "rngsubfloatoid")));
+ if (OidIsValid(subfloatOid))
+ appendPQExpBuffer(q, ",\n SUBTYPE_FLOAT = %s",
+ PQgetvalue(res, 0, PQfnumber(res, "rngsubfloat")));
+
+ appendPQExpBuffer(q, "\n);\n");
+
+ appendPQExpBuffer(labelq, "TYPE %s", fmtId(tyinfo->dobj.name));
+
+ if (binary_upgrade)
+ binary_upgrade_extension_member(q, &tyinfo->dobj, labelq->data);
+
+ ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
+ tyinfo->dobj.name,
+ tyinfo->dobj.namespace->dobj.name,
+ NULL,
+ tyinfo->rolname, false,
+ "TYPE", SECTION_PRE_DATA,
+ q->data, delq->data, NULL,
+ tyinfo->dobj.dependencies, tyinfo->dobj.nDeps,
+ NULL, NULL);
+
+ /* Dump Type Comments and Security Labels */
+ dumpComment(fout, labelq->data,
+ tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
+ tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
+ dumpSecLabel(fout, labelq->data,
+ tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
+ tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
+
+ PQclear(res);
+ destroyPQExpBuffer(q);
+ destroyPQExpBuffer(delq);
+ destroyPQExpBuffer(labelq);
+ destroyPQExpBuffer(query);
+ }
+
+ /*
* dumpBaseType
* writes out to fout the queries to recreate a user-defined base type
*/
*** a/src/include/catalog/catversion.h
--- b/src/include/catalog/catversion.h
***************
*** 53,58 ****
*/
/* yyyymmddN */
! #define CATALOG_VERSION_NO 201110221
#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,311 ----
DECLARE_UNIQUE_INDEX(pg_extension_name_index, 3081, on pg_extension using btree(extname name_ops));
#define ExtensionNameIndexId 3081
+ DECLARE_UNIQUE_INDEX(pg_range_rgntypid_index, 3542, on pg_range using btree(rngtypid oid_ops));
+ #define RangeTypidIndexId 3542
+
/* last step of initialization script: build the indexes declared above */
BUILD_INDICES
*** 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_ ));
***************
*** 4334,4339 **** DESCR("fetch the last row value");
--- 4340,4482 ----
DATA(insert OID = 3114 ( nth_value PGNSP PGUID 12 1 0 0 0 f t f t f i 2 0 2283 "2283 23" _null_ _null_ _null_ _null_ window_nth_value _null_ _null_ _null_ ));
DESCR("fetch the Nth row value");
+ /* procs for range types */
+ DATA(insert OID = 3832 ( anyrange_in PGNSP PGUID 12 1 0 0 0 f f f t f s 3 0 3831 "2275 26 23" _null_ _null_ _null_ _null_ anyrange_in _null_ _null_ _null_ ));
+ DESCR("I/O");
+ DATA(insert OID = 3833 ( anyrange_out PGNSP PGUID 12 1 0 0 0 f f f t f s 1 0 2275 "3831" _null_ _null_ _null_ _null_ anyrange_out _null_ _null_ _null_ ));
+ DESCR("I/O");
+ DATA(insert OID = 3834 ( range_in PGNSP PGUID 12 1 0 0 0 f f f t f s 3 0 3831 "2275 26 23" _null_ _null_ _null_ _null_ range_in _null_ _null_ _null_ ));
+ DESCR("I/O");
+ DATA(insert OID = 3835 ( range_out PGNSP PGUID 12 1 0 0 0 f f f t f s 1 0 2275 "3831" _null_ _null_ _null_ _null_ range_out _null_ _null_ _null_ ));
+ DESCR("I/O");
+ DATA(insert OID = 3836 ( range_recv PGNSP PGUID 12 1 0 0 0 f f f t f s 3 0 3831 "2281 26 23" _null_ _null_ _null_ _null_ range_recv _null_ _null_ _null_ ));
+ DESCR("I/O");
+ DATA(insert OID = 3837 ( range_send PGNSP PGUID 12 1 0 0 0 f f f t f s 1 0 17 "3831" _null_ _null_ _null_ _null_ range_send _null_ _null_ _null_ ));
+ DESCR("I/O");
+ DATA(insert OID = 3848 ( lower PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 2283 "3831" _null_ _null_ _null_ _null_ range_lower _null_ _null_ _null_ ));
+ DESCR("return the range's lower bound");
+ DATA(insert OID = 3849 ( upper PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 2283 "3831" _null_ _null_ _null_ _null_ range_upper _null_ _null_ _null_ ));
+ DESCR("return the range's upper bound");
+ DATA(insert OID = 3850 ( 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 = 3838 ( int4range PGNSP PGUID 12 1 0 0 0 f f f f f i 0 0 3904 "" _null_ _null_ _null_ _null_ range_constructor0 _null_ _null_ _null_ ));
+ DESCR("int4range constructor");
+ DATA(insert OID = 3839 ( int4range PGNSP PGUID 12 1 0 0 0 f f f f f i 1 0 3904 "23" _null_ _null_ _null_ _null_ range_constructor1 _null_ _null_ _null_ ));
+ DESCR("int4range constructor");
+ DATA(insert OID = 3840 ( int4range PGNSP PGUID 12 1 0 0 0 f f f f f i 2 0 3904 "23 23" _null_ _null_ _null_ _null_ range_constructor2 _null_ _null_ _null_ ));
+ DESCR("int4range constructor");
+ DATA(insert OID = 3841 ( int4range PGNSP PGUID 12 1 0 0 0 f f f f f i 3 0 3904 "23 23 25" _null_ _null_ _null_ _null_ range_constructor3 _null_ _null_ _null_ ));
+ DESCR("int4range constructor");
+ DATA(insert OID = 3842 ( numrange PGNSP PGUID 12 1 0 0 0 f f f f f i 0 0 3906 "" _null_ _null_ _null_ _null_ range_constructor0 _null_ _null_ _null_ ));
+ DESCR("int4range constructor");
+ DATA(insert OID = 3843 ( numrange PGNSP PGUID 12 1 0 0 0 f f f f f i 1 0 3906 "1700" _null_ _null_ _null_ _null_ range_constructor1 _null_ _null_ _null_ ));
+ DESCR("int4range constructor");
+ DATA(insert OID = 3844 ( numrange PGNSP PGUID 12 1 0 0 0 f f f f f i 2 0 3906 "1700 1700" _null_ _null_ _null_ _null_ range_constructor2 _null_ _null_ _null_ ));
+ DESCR("int4range constructor");
+ DATA(insert OID = 3845 ( numrange PGNSP PGUID 12 1 0 0 0 f f f f f i 3 0 3906 "1700 1700 25" _null_ _null_ _null_ _null_ range_constructor3 _null_ _null_ _null_ ));
+ DESCR("int4range constructor");
+ DATA(insert OID = 3846 ( tsrange PGNSP PGUID 12 1 0 0 0 f f f f f i 0 0 3908 "" _null_ _null_ _null_ _null_ range_constructor0 _null_ _null_ _null_ ));
+ DESCR("int4range constructor");
+ DATA(insert OID = 3847 ( tsrange PGNSP PGUID 12 1 0 0 0 f f f f f i 1 0 3908 "1114" _null_ _null_ _null_ _null_ range_constructor1 _null_ _null_ _null_ ));
+ DESCR("int4range constructor");
+ DATA(insert OID = 3933 ( tsrange PGNSP PGUID 12 1 0 0 0 f f f f f i 2 0 3908 "1114 1114" _null_ _null_ _null_ _null_ range_constructor2 _null_ _null_ _null_ ));
+ DESCR("int4range constructor");
+ DATA(insert OID = 3934 ( tsrange PGNSP PGUID 12 1 0 0 0 f f f f f i 3 0 3908 "1114 1114 25" _null_ _null_ _null_ _null_ range_constructor3 _null_ _null_ _null_ ));
+ DESCR("int4range constructor");
+ DATA(insert OID = 3935 ( tstzrange PGNSP PGUID 12 1 0 0 0 f f f f f i 0 0 3910 "" _null_ _null_ _null_ _null_ range_constructor0 _null_ _null_ _null_ ));
+ DESCR("int4range constructor");
+ DATA(insert OID = 3936 ( tstzrange PGNSP PGUID 12 1 0 0 0 f f f f f i 1 0 3910 "1184" _null_ _null_ _null_ _null_ range_constructor1 _null_ _null_ _null_ ));
+ DESCR("int4range constructor");
+ DATA(insert OID = 3937 ( tstzrange PGNSP PGUID 12 1 0 0 0 f f f f f i 2 0 3910 "1184 1184" _null_ _null_ _null_ _null_ range_constructor2 _null_ _null_ _null_ ));
+ DESCR("int4range constructor");
+ DATA(insert OID = 3938 ( tstzrange PGNSP PGUID 12 1 0 0 0 f f f f f i 3 0 3910 "1184 1184 25" _null_ _null_ _null_ _null_ range_constructor3 _null_ _null_ _null_ ));
+ DESCR("int4range constructor");
+ DATA(insert OID = 3939 ( daterange PGNSP PGUID 12 1 0 0 0 f f f f f i 0 0 3912 "" _null_ _null_ _null_ _null_ range_constructor0 _null_ _null_ _null_ ));
+ DESCR("int4range constructor");
+ DATA(insert OID = 3940 ( daterange PGNSP PGUID 12 1 0 0 0 f f f f f i 1 0 3912 "1082" _null_ _null_ _null_ _null_ range_constructor1 _null_ _null_ _null_ ));
+ DESCR("int4range constructor");
+ DATA(insert OID = 3941 ( daterange PGNSP PGUID 12 1 0 0 0 f f f f f i 2 0 3912 "1082 1082" _null_ _null_ _null_ _null_ range_constructor2 _null_ _null_ _null_ ));
+ DESCR("int4range constructor");
+ DATA(insert OID = 3942 ( daterange PGNSP PGUID 12 1 0 0 0 f f f f f i 3 0 3912 "1082 1082 25" _null_ _null_ _null_ _null_ range_constructor3 _null_ _null_ _null_ ));
+ DESCR("int4range constructor");
+ DATA(insert OID = 3943 ( int8range PGNSP PGUID 12 1 0 0 0 f f f f f i 0 0 3926 "" _null_ _null_ _null_ _null_ range_constructor0 _null_ _null_ _null_ ));
+ DESCR("int8range constructor");
+ DATA(insert OID = 3944 ( int8range PGNSP PGUID 12 1 0 0 0 f f f f f i 1 0 3926 "20" _null_ _null_ _null_ _null_ range_constructor1 _null_ _null_ _null_ ));
+ DESCR("int8range constructor");
+ DATA(insert OID = 3945 ( int8range PGNSP PGUID 12 1 0 0 0 f f f f f i 2 0 3926 "20 20" _null_ _null_ _null_ _null_ range_constructor2 _null_ _null_ _null_ ));
+ DESCR("int8range constructor");
+ DATA(insert OID = 3946 ( int8range PGNSP PGUID 12 1 0 0 0 f f f f f i 3 0 3926 "20 20 25" _null_ _null_ _null_ _null_ range_constructor3 _null_ _null_ _null_ ));
+ DESCR("int8range constructor");
/*
* Symbolic values for provolatile column: these indicate whether the result
*** /dev/null
--- b/src/include/catalog/pg_range.h
***************
*** 0 ****
--- 1,84 ----
+ /*-------------------------------------------------------------------------
+ *
+ * pg_range.h
+ * definition of the system "range" relation (pg_range)
+ * along with the relation's initial contents.
+ *
+ *
+ * Copyright (c) 2006-2010, PostgreSQL Global Development Group
+ *
+ * src/include/catalog/pg_range.h
+ *
+ * NOTES
+ * the genbki.pl script reads this file and generates .bki
+ * information from the DATA() statements.
+ *
+ * XXX do NOT break up DATA() statements into multiple lines!
+ * the scripts are not as smart as you might think...
+ *
+ *-------------------------------------------------------------------------
+ */
+ #ifndef PG_RANGE_H
+ #define PG_RANGE_H
+
+ #include "catalog/genbki.h"
+
+ /* ----------------
+ * pg_range definition. cpp turns this into
+ * typedef struct FormData_pg_range
+ * ----------------
+ */
+ #define RangeRelationId 3541
+
+ CATALOG(pg_range,3541) BKI_WITHOUT_OIDS
+ {
+ Oid rngtypid; /* OID of owning range type */
+ Oid rngsubtype; /* OID of range's subtype */
+ Oid rngcollation; /* collation for this range type, or 0 */
+ Oid rngsubopc; /* subtype's btree opclass */
+ regproc rngcanonical; /* canonicalize range, or 0 */
+ regproc 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
+
+ #define RANGE_DEFAULT_FLAGS "[)"
+
+ /*
+ * prototypes for functions in pg_range.c
+ */
+
+ void RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
+ Oid rangeSubOpclass, RegProcedure rangeCanonical,
+ RegProcedure 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
***************
*** 23,28 **** extern void DefineType(List *names, List *parameters);
--- 23,29 ----
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
***************
*** 346,351 **** typedef enum NodeTag
--- 346,352 ----
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
***************
*** 2335,2340 **** typedef struct AlterEnumStmt
--- 2335,2351 ----
} AlterEnumStmt;
/* ----------------------
+ * Create Type Statement, range types
+ * ----------------------
+ */
+ typedef struct CreateRangeStmt
+ {
+ NodeTag type;
+ List *typeName; /* qualified name (list of Value strings) */
+ List *params; /* range parameters (list of DefElem) */
+ } CreateRangeStmt;
+
+ /* ----------------------
* Create View Statement
* ----------------------
*/
*** 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
***************
*** 99,104 **** extern Oid get_rel_namespace(Oid relid);
--- 99,105 ----
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);
***************
*** 119,124 **** extern Node *get_typdefault(Oid typid);
--- 120,126 ----
extern char get_typtype(Oid typid);
extern bool type_is_rowtype(Oid typid);
extern bool type_is_enum(Oid typid);
+ extern bool type_is_range(Oid typid);
extern void get_type_category_preferred(Oid typid,
char *typcategory,
bool *typispreferred);
***************
*** 147,152 **** extern void free_attstatsslot(Oid atttype,
--- 149,159 ----
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_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,153 ----
+ /*-------------------------------------------------------------------------
+ *
+ * rangetypes.h
+ * Declarations for Postgres range types.
+ *
+ */
+
+ #ifndef RANGETYPES_H
+ #define RANGETYPES_H
+
+ #include "fmgr.h"
+
+ typedef struct varlena RangeType;
+
+ typedef struct
+ {
+ Datum val;
+ Oid rngtypid;
+ bool infinite;
+ bool lower;
+ bool inclusive;
+ } RangeBound;
+
+ typedef struct
+ {
+ FmgrInfo canonicalFn;
+ FmgrInfo cmpFn;
+ FmgrInfo subfloatFn;
+ Oid rngtypid;
+ Oid subtype;
+ Oid collation;
+ int16 subtyplen;
+ char subtypalign;
+ char subtypstorage;
+ bool subtypbyval;
+ } RangeTypeInfo;
+
+ /*
+ * fmgr macros for range type objects
+ */
+ #define DatumGetRangeType(X) ((RangeType *) PG_DETOAST_DATUM(X))
+ #define DatumGetRangeTypeCopy(X) ((RangeType *) PG_DETOAST_DATUM_COPY(X))
+ #define RangeTypeGetDatum(X) PointerGetDatum(X)
+ #define PG_GETARG_RANGE(n) DatumGetRangeType(PG_GETARG_DATUM(n))
+ #define PG_GETARG_RANGE_COPY(n) DatumGetRangeTypeCopy(PG_GETARG_DATUM(n))
+ #define PG_RETURN_RANGE(x) return RangeTypeGetDatum(x)
+
+ /*
+ * prototypes for functions defined in rangetypes.c
+ */
+
+ /* IO */
+ extern Datum anyrange_in(PG_FUNCTION_ARGS);
+ extern Datum anyrange_out(PG_FUNCTION_ARGS);
+ extern Datum range_in(PG_FUNCTION_ARGS);
+ extern Datum range_out(PG_FUNCTION_ARGS);
+ extern Datum range_recv(PG_FUNCTION_ARGS);
+ extern Datum range_send(PG_FUNCTION_ARGS);
+
+ /* constructors */
+ extern Datum range_constructor0(PG_FUNCTION_ARGS);
+ extern Datum range_constructor1(PG_FUNCTION_ARGS);
+ extern Datum range_constructor2(PG_FUNCTION_ARGS);
+ extern Datum range_constructor3(PG_FUNCTION_ARGS);
+ extern Datum range_make1(PG_FUNCTION_ARGS);
+ extern Datum range_linf_(PG_FUNCTION_ARGS);
+ extern Datum range_uinf_(PG_FUNCTION_ARGS);
+ extern Datum range_linfi(PG_FUNCTION_ARGS);
+ extern Datum range_uinfi(PG_FUNCTION_ARGS);
+ extern Datum range(PG_FUNCTION_ARGS);
+ extern Datum range__(PG_FUNCTION_ARGS);
+ extern Datum range_i(PG_FUNCTION_ARGS);
+ extern Datum rangei_(PG_FUNCTION_ARGS);
+ extern Datum rangeii(PG_FUNCTION_ARGS);
+
+ /* range -> subtype */
+ extern Datum range_lower(PG_FUNCTION_ARGS);
+ extern Datum range_upper(PG_FUNCTION_ARGS);
+
+ /* range -> bool */
+ extern Datum range_empty(PG_FUNCTION_ARGS);
+ extern Datum range_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(FunctionCallInfo fcinfo, RangeBound *lower,
+ RangeBound *upper, bool empty);
+ extern void range_deserialize(FunctionCallInfo fcinfo, RangeType *range,
+ RangeBound *lower, RangeBound *upper,
+ bool *empty);
+ extern int range_cmp_bounds(FunctionCallInfo fcinfo, RangeBound *b1,
+ RangeBound *b2);
+ extern RangeType *make_empty_range(FunctionCallInfo fcinfo, Oid rngtypid);
+ extern void range_gettypinfo(FunctionCallInfo fcinfo, Oid rngtypid,
+ RangeTypeInfo *rngtypinfo);
+
+ /* for defining a range "canonicalize" function */
+ extern Datum range_serialize(FunctionCallInfo fcinfo, RangeBound *lower,
+ RangeBound *upper, bool empty);
+
+ /* for use in DefineRange */
+ extern char range_parse_flags(char *flags_str);
+
+ #endif /* RANGETYPES_H */
*** a/src/include/utils/syscache.h
--- b/src/include/utils/syscache.h
***************
*** 70,75 **** enum SysCacheIdentifier
--- 70,76 ----
OPFAMILYOID,
PROCNAMEARGSNSP,
PROCOID,
+ RANGETYPE,
RELNAMENSP,
RELOID,
RULERELNAME,
*** a/src/include/utils/timestamp.h
--- b/src/include/utils/timestamp.h
***************
*** 158,163 **** extern Datum interval_part(PG_FUNCTION_ARGS);
--- 158,164 ----
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
***************
*** 490,495 **** do_compile(FunctionCallInfo fcinfo,
--- 490,497 ----
{
if (rettypeid == ANYARRAYOID)
rettypeid = INT4ARRAYOID;
+ else if (rettypeid == ANYRANGEOID)
+ rettypeid = INT4RANGEOID;
else /* ANYELEMENT or ANYNONARRAY */
rettypeid = INT4OID;
/* XXX what could we use for ANYENUM? */
***************
*** 2119,2124 **** build_datatype(HeapTuple typeTup, int32 typmod, Oid collation)
--- 2121,2127 ----
case TYPTYPE_BASE:
case TYPTYPE_DOMAIN:
case TYPTYPE_ENUM:
+ case TYPTYPE_RANGE:
typ->ttype = PLPGSQL_TTYPE_SCALAR;
break;
case TYPTYPE_COMPOSITE:
***************
*** 2373,2380 **** 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,
--- 2376,2383 ----
/*
* 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,
***************
*** 2407,2412 **** plpgsql_resolve_polymorphic_argtypes(int numargs,
--- 2410,2418 ----
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/collate.linux.utf8.out
--- b/src/test/regress/expected/collate.linux.utf8.out
***************
*** 1049,1051 **** Composite type "public.collate_dep_test2"
--- 1049,1068 ----
DROP TABLE collate_dep_test1, collate_dep_test4t;
DROP TYPE collate_dep_test2;
+ -- test range types and collations
+ create type textrange_c as range(subtype=text, collation="C");
+ create type textrange_en_us as range(subtype=text, collation="en_US");
+ select textrange_c('A','Z') @> 'b'::text;
+ ?column?
+ ----------
+ f
+ (1 row)
+
+ select textrange_en_us('A','Z') @> 'b'::text;
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ drop type textrange_c;
+ drop type textrange_en_us;
*** a/src/test/regress/expected/opr_sanity.out
--- b/src/test/regress/expected/opr_sanity.out
***************
*** 153,159 **** ORDER BY 1, 2;
------------+------------
25 | 1043
1114 | 1184
! (2 rows)
SELECT DISTINCT p1.proargtypes[0], p2.proargtypes[0]
FROM pg_proc AS p1, pg_proc AS p2
--- 153,174 ----
------------+------------
25 | 1043
1114 | 1184
! 3904 | 3906
! 3904 | 3908
! 3904 | 3910
! 3904 | 3912
! 3904 | 3926
! 3906 | 3908
! 3906 | 3910
! 3906 | 3912
! 3906 | 3926
! 3908 | 3910
! 3908 | 3912
! 3908 | 3926
! 3910 | 3912
! 3910 | 3926
! 3912 | 3926
! (17 rows)
SELECT DISTINCT p1.proargtypes[0], p2.proargtypes[0]
FROM pg_proc AS p1, pg_proc AS p2
***************
*** 165,176 **** WHERE p1.oid != p2.oid AND
ORDER BY 1, 2;
proargtypes | proargtypes
-------------+-------------
25 | 1042
25 | 1043
1114 | 1184
1560 | 1562
2277 | 2283
! (5 rows)
SELECT DISTINCT p1.proargtypes[1], p2.proargtypes[1]
FROM pg_proc AS p1, pg_proc AS p2
--- 180,205 ----
ORDER BY 1, 2;
proargtypes | proargtypes
-------------+-------------
+ 20 | 23
+ 20 | 1082
+ 20 | 1114
+ 20 | 1184
+ 20 | 1700
+ 23 | 1082
+ 23 | 1114
+ 23 | 1184
+ 23 | 1700
25 | 1042
25 | 1043
+ 1082 | 1114
+ 1082 | 1184
+ 1082 | 1700
1114 | 1184
+ 1114 | 1700
+ 1184 | 1700
1560 | 1562
2277 | 2283
! (19 rows)
SELECT DISTINCT p1.proargtypes[1], p2.proargtypes[1]
FROM pg_proc AS p1, pg_proc AS p2
***************
*** 182,192 **** WHERE p1.oid != p2.oid AND
ORDER BY 1, 2;
proargtypes | proargtypes
-------------+-------------
23 | 28
1114 | 1184
1560 | 1562
2277 | 2283
! (4 rows)
SELECT DISTINCT p1.proargtypes[2], p2.proargtypes[2]
FROM pg_proc AS p1, pg_proc AS p2
--- 211,235 ----
ORDER BY 1, 2;
proargtypes | proargtypes
-------------+-------------
+ 20 | 23
+ 20 | 1082
+ 20 | 1114
+ 20 | 1184
+ 20 | 1700
23 | 28
+ 23 | 1082
+ 23 | 1114
+ 23 | 1184
+ 23 | 1700
+ 1082 | 1114
+ 1082 | 1184
+ 1082 | 1700
1114 | 1184
+ 1114 | 1700
+ 1184 | 1700
1560 | 1562
2277 | 2283
! (18 rows)
SELECT DISTINCT p1.proargtypes[2], p2.proargtypes[2]
FROM pg_proc AS p1, pg_proc AS p2
***************
*** 1015,1033 **** ORDER BY 1, 2, 3;
--- 1058,1087 ----
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
--- 1098,1104 ----
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
*** a/src/test/regress/expected/plpgsql.out
--- b/src/test/regress/expected/plpgsql.out
***************
*** 4571,4573 **** ERROR: value for domain orderedarray violates check constraint "sorted"
--- 4571,4582 ----
CONTEXT: PL/pgSQL function "testoa" line 5 at assignment
drop function arrayassign1();
drop function testoa(x1 int, x2 int, x3 int);
+ -- defined in rangetypes.sql
+ -- test is designed to exercise resolve_polymorphic_argtypes, which is only
+ -- called when invoked from a different backend from which it's defined.
+ select rangetypes_plpgsql(int4range(1,10),ARRAY[2,20]);
+ rangetypes_plpgsql
+ --------------------
+ 12
+ (1 row)
+
*** /dev/null
--- b/src/test/regress/expected/rangetypes.out
***************
*** 0 ****
--- 1,938 ----
+ --
+ -- test parser
+ --
+ create type textrange as range (subtype=text, collation="C");
+ -- negative tests; should fail
+ select ''::textrange;
+ ERROR: malformed range literal: ""
+ LINE 1: select ''::textrange;
+ ^
+ DETAIL: Missing left parenthesis or bracket.
+ select '-[a,z)'::textrange;
+ ERROR: malformed range literal: "-[a,z)"
+ LINE 1: select '-[a,z)'::textrange;
+ ^
+ DETAIL: Missing left parenthesis or bracket.
+ select '[a,z) - '::textrange;
+ ERROR: malformed range literal: "[a,z) - "
+ LINE 1: select '[a,z) - '::textrange;
+ ^
+ DETAIL: Junk after right parenthesis or bracket.
+ select '(",a)'::textrange;
+ ERROR: malformed range literal: "(",a)"
+ LINE 1: select '(",a)'::textrange;
+ ^
+ DETAIL: Unexpected end of input.
+ select '(,,a)'::textrange;
+ ERROR: malformed range literal: "(,,a)"
+ LINE 1: select '(,,a)'::textrange;
+ ^
+ DETAIL: Too many boundaries.
+ select '(),a)'::textrange;
+ ERROR: malformed range literal: "(),a)"
+ LINE 1: select '(),a)'::textrange;
+ ^
+ DETAIL: Missing upper bound.
+ select '(a,))'::textrange;
+ ERROR: malformed range literal: "(a,))"
+ LINE 1: select '(a,))'::textrange;
+ ^
+ DETAIL: Junk after right parenthesis or bracket.
+ select '(],a)'::textrange;
+ ERROR: malformed range literal: "(],a)"
+ LINE 1: select '(],a)'::textrange;
+ ^
+ DETAIL: Missing upper bound.
+ select '(a,])'::textrange;
+ ERROR: malformed range literal: "(a,])"
+ LINE 1: select '(a,])'::textrange;
+ ^
+ DETAIL: Junk after right parenthesis or bracket.
+ -- should succeed
+ select ' empty '::textrange;
+ textrange
+ -----------
+ empty
+ (1 row)
+
+ select ' ( empty, empty ) '::textrange;
+ textrange
+ ----------------------
+ (" empty"," empty ")
+ (1 row)
+
+ select ' ( " a " " a ", " z " " z " ) '::textrange;
+ textrange
+ --------------------------
+ (" a a "," z z ")
+ (1 row)
+
+ select '(,z)'::textrange;
+ textrange
+ -----------
+ (,z)
+ (1 row)
+
+ select '(a,)'::textrange;
+ textrange
+ -----------
+ (a,)
+ (1 row)
+
+ select '[,z]'::textrange;
+ textrange
+ -----------
+ (,z]
+ (1 row)
+
+ select '[a,]'::textrange;
+ textrange
+ -----------
+ [a,)
+ (1 row)
+
+ select '( , )'::textrange;
+ textrange
+ -----------
+ (" "," ")
+ (1 row)
+
+ select '("","")'::textrange;
+ textrange
+ -----------
+ ("","")
+ (1 row)
+
+ select '["",""]'::textrange;
+ textrange
+ -----------
+ ["",""]
+ (1 row)
+
+ select '(",",",")'::textrange;
+ textrange
+ -----------
+ (",",",")
+ (1 row)
+
+ select '("\\","\\")'::textrange
+ select '(\\,a)'::textrange;
+ ERROR: syntax error at or near "select"
+ LINE 2: select '(\\,a)'::textrange;
+ ^
+ select '((,z)'::textrange;
+ textrange
+ -----------
+ ("(",z)
+ (1 row)
+
+ select '([,z)'::textrange;
+ textrange
+ -----------
+ ("[",z)
+ (1 row)
+
+ select '(!,()'::textrange;
+ textrange
+ -----------
+ (!,"(")
+ (1 row)
+
+ select '(!,[)'::textrange;
+ textrange
+ -----------
+ (!,"[")
+ (1 row)
+
+ drop type textrange;
+ --
+ -- create some test data and test the operators
+ --
+ CREATE TABLE numrange_test (nr NUMRANGE);
+ create index numrange_test_btree on numrange_test(nr);
+ SET enable_seqscan = f;
+ INSERT INTO numrange_test VALUES('[,)');
+ INSERT INTO numrange_test VALUES('[3,]');
+ INSERT INTO numrange_test VALUES('[, 5)');
+ INSERT INTO numrange_test VALUES(numrange(1.1, 2.2));
+ INSERT INTO numrange_test VALUES('empty');
+ INSERT INTO numrange_test VALUES(numrange(1.7));
+ SELECT 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, numrange(1.9,1.91));
+ nr
+ -----------
+ (,)
+ (,5)
+ [1.1,2.2)
+ (3 rows)
+
+ SELECT * FROM numrange_test WHERE nr @> numrange(1.0,10000.1);
+ nr
+ -----
+ (,)
+ (1 row)
+
+ SELECT * FROM numrange_test WHERE contained_by(numrange(-1e7,-10000.1), nr);
+ nr
+ ------
+ (,)
+ (,5)
+ (2 rows)
+
+ SELECT * FROM numrange_test WHERE 1.9 <@ nr;
+ nr
+ -----------
+ (,)
+ (,5)
+ [1.1,2.2)
+ (3 rows)
+
+ SELECT * FROM numrange_test WHERE nr = 'empty';
+ nr
+ -------
+ empty
+ (1 row)
+
+ SELECT * FROM numrange_test WHERE range_eq(nr, '(1.1, 2.2)');
+ nr
+ ----
+ (0 rows)
+
+ SELECT * FROM numrange_test WHERE nr = '[1.1, 2.2)';
+ nr
+ -----------
+ [1.1,2.2)
+ (1 row)
+
+ select numrange(2.0, 1.0);
+ ERROR: range lower bound must be less than or equal to range upper bound
+ select numrange(2.0, 3.0) -|- numrange(3.0, 4.0);
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ select adjacent(numrange(2.0, 3.0), numrange(3.1, 4.0));
+ adjacent
+ ----------
+ f
+ (1 row)
+
+ select numrange(2.0, 3.0, '[]') -|- numrange(3.0, 4.0, '()');
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ select numrange(1.0, 2.0) -|- numrange(2.0, 3.0,'[]');
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ select adjacent(numrange(2.0, 3.0, '(]'), numrange(1.0, 2.0, '(]'));
+ adjacent
+ ----------
+ t
+ (1 row)
+
+ select numrange(1.1, 3.3) <@ numrange(0.1,10.1);
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ select numrange(0.1, 10.1) <@ numrange(1.1,3.3);
+ ?column?
+ ----------
+ f
+ (1 row)
+
+ select numrange(1.1, 2.2) - numrange(2.0, 3.0);
+ ?column?
+ -----------
+ [1.1,2.0)
+ (1 row)
+
+ select numrange(1.1, 2.2) - numrange(2.2, 3.0);
+ ?column?
+ -----------
+ [1.1,2.2)
+ (1 row)
+
+ select numrange(1.1, 2.2,'[]') - numrange(2.0, 3.0);
+ ?column?
+ -----------
+ [1.1,2.0)
+ (1 row)
+
+ select minus(numrange(10.1,12.2,'[]'), numrange(110.0,120.2,'(]'));
+ minus
+ -------------
+ [10.1,12.2]
+ (1 row)
+
+ select minus(numrange(10.1,12.2,'[]'), numrange(0.0,120.2,'(]'));
+ minus
+ -------
+ empty
+ (1 row)
+
+ select numrange(4.5, 5.5, '[]') && numrange(5.5, 6.5);
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ select numrange(1.0, 2.0) << numrange(3.0, 4.0);
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ select numrange(1.0, 2.0) >> numrange(3.0, 4.0);
+ ?column?
+ ----------
+ f
+ (1 row)
+
+ select numrange(3.0, 70.0) &< numrange(6.6, 100.0);
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ select numrange(1.1, 2.2) < numrange(1.0, 200.2);
+ ?column?
+ ----------
+ f
+ (1 row)
+
+ select numrange(1.1, 2.2) < numrange(1.1, 1.2);
+ ?column?
+ ----------
+ f
+ (1 row)
+
+ select numrange(1.0, 2.0) + numrange(2.0, 3.0);
+ ?column?
+ -----------
+ [1.0,3.0)
+ (1 row)
+
+ select numrange(1.0, 2.0) + numrange(1.5, 3.0);
+ ?column?
+ -----------
+ [1.0,3.0)
+ (1 row)
+
+ select numrange(1.0, 2.0) + numrange(2.5, 3.0);
+ ERROR: result range is not contiguous
+ select numrange(1.0, 2.0) * numrange(2.0, 3.0);
+ ?column?
+ ----------
+ empty
+ (1 row)
+
+ select numrange(1.0, 2.0) * numrange(1.5, 3.0);
+ ?column?
+ -----------
+ [1.5,2.0)
+ (1 row)
+
+ select numrange(1.0, 2.0) * numrange(2.5, 3.0);
+ ?column?
+ ----------
+ empty
+ (1 row)
+
+ select * from numrange_test where nr < numrange(-1000.0, -1000.0,'[]');
+ nr
+ -------
+ (,)
+ (,5)
+ empty
+ (3 rows)
+
+ select * from numrange_test where nr < numrange(0.0, 1.0,'[]');
+ nr
+ -------
+ (,)
+ (,5)
+ empty
+ (3 rows)
+
+ select * from numrange_test where nr < numrange(1000.0, 1001.0,'[]');
+ nr
+ -----------
+ (,)
+ [3,)
+ (,5)
+ [1.1,2.2)
+ empty
+ [1.7,1.7]
+ (6 rows)
+
+ select * from numrange_test where nr > numrange(-1001.0, -1000.0,'[]');
+ nr
+ -----------
+ [3,)
+ [1.1,2.2)
+ [1.7,1.7]
+ (3 rows)
+
+ select * from numrange_test where nr > numrange(0.0, 1.0,'[]');
+ nr
+ -----------
+ [3,)
+ [1.1,2.2)
+ [1.7,1.7]
+ (3 rows)
+
+ select * from numrange_test where nr > numrange(1000.0, 1000.0,'[]');
+ nr
+ ----
+ (0 rows)
+
+ create table numrange_test2(nr numrange);
+ create index numrange_test2_hash_idx on numrange_test2 (nr);
+ INSERT INTO numrange_test2 VALUES('[, 5)');
+ INSERT INTO numrange_test2 VALUES(numrange(1.1, 2.2));
+ INSERT INTO numrange_test2 VALUES(numrange(1.1, 2.2));
+ INSERT INTO numrange_test2 VALUES(numrange(1.1, 2.2,'()'));
+ INSERT INTO numrange_test2 VALUES('empty');
+ select * from numrange_test2 where nr = 'empty'::numrange;
+ nr
+ -------
+ empty
+ (1 row)
+
+ select * from numrange_test2 where nr = numrange(1.1, 2.2);
+ nr
+ -----------
+ [1.1,2.2)
+ [1.1,2.2)
+ (2 rows)
+
+ select * from numrange_test2 where nr = numrange(1.1, 2.3);
+ nr
+ ----
+ (0 rows)
+
+ set enable_nestloop=t;
+ set enable_hashjoin=f;
+ set enable_mergejoin=f;
+ select * from numrange_test natural join numrange_test2 order by nr;
+ nr
+ -----------
+ empty
+ (,5)
+ [1.1,2.2)
+ [1.1,2.2)
+ (4 rows)
+
+ set enable_nestloop=f;
+ set enable_hashjoin=t;
+ set enable_mergejoin=f;
+ select * from numrange_test natural join numrange_test2 order by nr;
+ nr
+ -----------
+ empty
+ (,5)
+ [1.1,2.2)
+ [1.1,2.2)
+ (4 rows)
+
+ set enable_nestloop=f;
+ set enable_hashjoin=f;
+ set enable_mergejoin=t;
+ select * from numrange_test natural join numrange_test2 order by nr;
+ nr
+ -----------
+ empty
+ (,5)
+ [1.1,2.2)
+ [1.1,2.2)
+ (4 rows)
+
+ set enable_nestloop to default;
+ set enable_hashjoin to default;
+ set enable_mergejoin to default;
+ SET enable_seqscan TO DEFAULT;
+ DROP TABLE numrange_test;
+ DROP TABLE numrange_test2;
+ -- test canonical form for int4range
+ select int4range(1,10,'[]');
+ int4range
+ -----------
+ [1,11)
+ (1 row)
+
+ select int4range(1,10,'[)');
+ int4range
+ -----------
+ [1,10)
+ (1 row)
+
+ select int4range(1,10,'(]');
+ int4range
+ -----------
+ [2,11)
+ (1 row)
+
+ select int4range(1,10,'[]');
+ int4range
+ -----------
+ [1,11)
+ (1 row)
+
+ -- test canonical form for daterange
+ select daterange('2000-01-10'::date, '2000-01-20'::date,'[]');
+ daterange
+ -------------------------
+ [01-10-2000,01-21-2000)
+ (1 row)
+
+ select daterange('2000-01-10'::date, '2000-01-20'::date,'[)');
+ daterange
+ -------------------------
+ [01-10-2000,01-20-2000)
+ (1 row)
+
+ select daterange('2000-01-10'::date, '2000-01-20'::date,'(]');
+ daterange
+ -------------------------
+ [01-11-2000,01-21-2000)
+ (1 row)
+
+ select daterange('2000-01-10'::date, '2000-01-20'::date,'[]');
+ daterange
+ -------------------------
+ [01-10-2000,01-21-2000)
+ (1 row)
+
+ create table test_range_gist(ir int4range);
+ create index test_range_gist_idx on test_range_gist using gist (ir);
+ insert into test_range_gist select int4range(g, g+10) from generate_series(1,2000) g;
+ insert into test_range_gist select 'empty'::int4range from generate_series(1,500) g;
+ insert into test_range_gist select int4range(g, g+10000) from generate_series(1,1000) g;
+ insert into test_range_gist select 'empty'::int4range from generate_series(1,500) g;
+ insert into test_range_gist select int4range(NULL,g*10,'(]') from generate_series(1,100) g;
+ insert into test_range_gist select int4range(g*10,NULL,'(]') from generate_series(1,100) g;
+ insert into test_range_gist select int4range(g, g+10) from generate_series(1,2000) g;
+ BEGIN;
+ SET LOCAL enable_seqscan = t;
+ SET LOCAL enable_bitmapscan = f;
+ SET LOCAL enable_indexscan = f;
+ select count(*) from test_range_gist where ir @> 'empty'::int4range;
+ count
+ -------
+ 6200
+ (1 row)
+
+ select count(*) from test_range_gist where ir = int4range(10,20);
+ count
+ -------
+ 2
+ (1 row)
+
+ select count(*) from test_range_gist where ir @> 10;
+ count
+ -------
+ 130
+ (1 row)
+
+ select count(*) from test_range_gist where ir @> int4range(10,20);
+ count
+ -------
+ 111
+ (1 row)
+
+ select count(*) from test_range_gist where ir && int4range(10,20);
+ count
+ -------
+ 158
+ (1 row)
+
+ select count(*) from test_range_gist where ir <@ int4range(10,50);
+ count
+ -------
+ 1062
+ (1 row)
+
+ select count(*) from (select * from test_range_gist where ir?) s where ir << int4range(100,500);
+ count
+ -------
+ 189
+ (1 row)
+
+ select count(*) from (select * from test_range_gist where ir?) s where ir >> int4range(100,500);
+ count
+ -------
+ 3554
+ (1 row)
+
+ select count(*) from (select * from test_range_gist where ir?) s where ir &< int4range(100,500);
+ count
+ -------
+ 1029
+ (1 row)
+
+ select count(*) from (select * from test_range_gist where ir?) s where ir &> int4range(100,500);
+ count
+ -------
+ 4794
+ (1 row)
+
+ select count(*) from (select * from test_range_gist where ir?) s where ir -|- int4range(100,500);
+ count
+ -------
+ 5
+ (1 row)
+
+ COMMIT;
+ BEGIN;
+ SET LOCAL enable_seqscan = f;
+ SET LOCAL enable_bitmapscan = f;
+ SET LOCAL enable_indexscan = t;
+ select count(*) from test_range_gist where ir @> 'empty'::int4range;
+ count
+ -------
+ 6200
+ (1 row)
+
+ select count(*) from test_range_gist where ir = int4range(10,20);
+ count
+ -------
+ 2
+ (1 row)
+
+ select count(*) from test_range_gist where ir @> 10;
+ count
+ -------
+ 130
+ (1 row)
+
+ select count(*) from test_range_gist where ir @> int4range(10,20);
+ count
+ -------
+ 111
+ (1 row)
+
+ select count(*) from test_range_gist where ir && int4range(10,20);
+ count
+ -------
+ 158
+ (1 row)
+
+ select count(*) from test_range_gist where ir <@ int4range(10,50);
+ count
+ -------
+ 1062
+ (1 row)
+
+ select count(*) from test_range_gist where ir << int4range(100,500);
+ count
+ -------
+ 189
+ (1 row)
+
+ select count(*) from test_range_gist where ir >> int4range(100,500);
+ count
+ -------
+ 3554
+ (1 row)
+
+ select count(*) from test_range_gist where ir &< int4range(100,500);
+ count
+ -------
+ 1029
+ (1 row)
+
+ select count(*) from test_range_gist where ir &> int4range(100,500);
+ count
+ -------
+ 4794
+ (1 row)
+
+ select count(*) from test_range_gist where ir -|- int4range(100,500);
+ count
+ -------
+ 5
+ (1 row)
+
+ COMMIT;
+ drop index test_range_gist_idx;
+ create index test_range_gist_idx on test_range_gist using gist (ir);
+ BEGIN;
+ SET LOCAL enable_seqscan = f;
+ SET LOCAL enable_bitmapscan = f;
+ SET LOCAL enable_indexscan = t;
+ select count(*) from test_range_gist where ir @> 'empty'::int4range;
+ count
+ -------
+ 6200
+ (1 row)
+
+ select count(*) from test_range_gist where ir = int4range(10,20);
+ count
+ -------
+ 2
+ (1 row)
+
+ select count(*) from test_range_gist where ir @> 10;
+ count
+ -------
+ 130
+ (1 row)
+
+ select count(*) from test_range_gist where ir @> int4range(10,20);
+ count
+ -------
+ 111
+ (1 row)
+
+ select count(*) from test_range_gist where ir && int4range(10,20);
+ count
+ -------
+ 158
+ (1 row)
+
+ select count(*) from test_range_gist where ir <@ int4range(10,50);
+ count
+ -------
+ 1062
+ (1 row)
+
+ select count(*) from test_range_gist where ir << int4range(100,500);
+ count
+ -------
+ 189
+ (1 row)
+
+ select count(*) from test_range_gist where ir >> int4range(100,500);
+ count
+ -------
+ 3554
+ (1 row)
+
+ select count(*) from test_range_gist where ir &< int4range(100,500);
+ count
+ -------
+ 1029
+ (1 row)
+
+ select count(*) from test_range_gist where ir &> int4range(100,500);
+ count
+ -------
+ 4794
+ (1 row)
+
+ select count(*) from test_range_gist where ir -|- int4range(100,500);
+ count
+ -------
+ 5
+ (1 row)
+
+ COMMIT;
+ drop table test_range_gist;
+ --
+ -- Btree_gist is not included by default, so to test exclusion
+ -- constraints with range types, use singleton int ranges for the "="
+ -- portion of the constraint.
+ --
+ create table test_range_excl(
+ room int4range,
+ speaker int4range,
+ during tsrange,
+ exclude using gist (room with =, during with &&),
+ exclude using gist (speaker with =, during with &&)
+ );
+ NOTICE: CREATE TABLE / EXCLUDE will create implicit index "test_range_excl_room_during_excl" for table "test_range_excl"
+ NOTICE: CREATE TABLE / EXCLUDE will create implicit index "test_range_excl_speaker_during_excl" for table "test_range_excl"
+ insert into test_range_excl
+ values(int4range(123), int4range(1), '[2010-01-02 10:00, 2010-01-02 11:00)');
+ insert into test_range_excl
+ values(int4range(123), int4range(2), '[2010-01-02 11:00, 2010-01-02 12:00)');
+ insert into test_range_excl
+ values(int4range(123), int4range(3), '[2010-01-02 10:10, 2010-01-02 11:10)');
+ ERROR: conflicting key value violates exclusion constraint "test_range_excl_room_during_excl"
+ DETAIL: Key (room, during)=([123,124), ["Sat Jan 02 10:10:00 2010","Sat Jan 02 11:10:00 2010")) conflicts with existing key (room, during)=([123,124), ["Sat Jan 02 10:00:00 2010","Sat Jan 02 11:00:00 2010")).
+ insert into test_range_excl
+ values(int4range(124), int4range(3), '[2010-01-02 10:10, 2010-01-02 11:10)');
+ insert into test_range_excl
+ values(int4range(125), int4range(1), '[2010-01-02 10:10, 2010-01-02 11:10)');
+ ERROR: conflicting key value violates exclusion constraint "test_range_excl_speaker_during_excl"
+ DETAIL: Key (speaker, during)=([1,2), ["Sat Jan 02 10:10:00 2010","Sat Jan 02 11:10:00 2010")) conflicts with existing key (speaker, during)=([1,2), ["Sat Jan 02 10:00:00 2010","Sat Jan 02 11:00:00 2010")).
+ drop table test_range_excl;
+ -- test bigint ranges
+ select int8range(10000000000::int8, 20000000000::int8,'(]');
+ int8range
+ ---------------------------
+ [10000000001,20000000001)
+ (1 row)
+
+ -- test tstz ranges
+ set timezone to '-08';
+ select '[2010-01-01 01:00:00 -05, 2010-01-01 02:00:00 -08)'::tstzrange;
+ tstzrange
+ -----------------------------------------------------------------
+ ["Thu Dec 31 22:00:00 2009 -08","Fri Jan 01 02:00:00 2010 -08")
+ (1 row)
+
+ -- should fail
+ select '[2010-01-01 01:00:00 -08, 2010-01-01 02:00:00 -05)'::tstzrange;
+ ERROR: range lower bound must be less than or equal to range upper bound
+ LINE 1: select '[2010-01-01 01:00:00 -08, 2010-01-01 02:00:00 -05)':...
+ ^
+ set timezone to default;
+ --
+ -- Test user-defined range of floats
+ --
+ 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(float8range(-100.00007, '1.111113e9'));
+ select * from float8range_test;
+ f8r | i
+ -------------------------+---
+ [-100.00007,1111113000) |
+ (1 row)
+
+ drop table float8range_test;
+ drop type float8range;
+ --
+ -- 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)
+
+ drop type mydomainrange;
+ drop domain mydomain;
+ --
+ -- Test multiple range types over the same subtype
+ --
+ create type textrange1 as range(subtype=text, collation="C");
+ create type textrange2 as range(subtype=text, collation="C");
+ select textrange1('a','Z') @> 'b'::text;
+ ERROR: range lower bound must be less than or equal to range upper bound
+ select textrange2('a','z') @> 'b'::text;
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ drop type textrange1;
+ drop type textrange2;
+ --
+ -- Test out polymorphic type system
+ --
+ create function anyarray_anyrange_func(a anyarray, r anyrange)
+ returns anyelement as 'select $1[1] + lower($2);' language sql;
+ select anyarray_anyrange_func(ARRAY[1,2], int4range(10,20));
+ anyarray_anyrange_func
+ ------------------------
+ 11
+ (1 row)
+
+ -- should fail
+ select anyarray_anyrange_func(ARRAY[1,2], numrange(10,20));
+ ERROR: function anyarray_anyrange_func(integer[], numrange) does not exist
+ LINE 1: select anyarray_anyrange_func(ARRAY[1,2], numrange(10,20));
+ ^
+ HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+ drop function anyarray_anyrange_func(anyarray, anyrange);
+ -- should fail
+ create function bogus_func(anyelement)
+ returns anyrange as 'select int4range(1,10)' language sql;
+ ERROR: cannot determine result data type
+ DETAIL: A function returning ANYRANGE must have at least one ANYRANGE argument.
+ -- should fail
+ create function bogus_func(int)
+ returns anyrange as 'select int4range(1,10)' language sql;
+ ERROR: cannot determine result data type
+ DETAIL: A function returning a polymorphic type must have at least one polymorphic argument.
+ create function range_add_bounds(anyrange)
+ returns anyelement as 'select lower($1) + upper($1)' language sql;
+ select range_add_bounds(numrange(1.0001, 123.123));
+ range_add_bounds
+ ------------------
+ 124.1231
+ (1 row)
+
+ --
+ -- Arrays of ranges
+ --
+ select ARRAY[numrange(1.1), numrange(12.3,155.5)];
+ array
+ ------------------------------
+ {"[1.1,1.1]","[12.3,155.5)"}
+ (1 row)
+
+ --
+ -- Ranges of arrays
+ --
+ create type arrayrange as range (subtype=int4[]);
+ select arrayrange(ARRAY[1,2], ARRAY[2,1]);
+ arrayrange
+ -------------------
+ ["{1,2}","{2,1}")
+ (1 row)
+
+ drop type arrayrange;
+ --
+ -- OUT/INOUT/TABLE functions
+ --
+ create function outparam_succeed(i anyrange, out r anyrange, out t text)
+ as $$ select $1, 'foo' $$ language sql;
+ create function inoutparam_succeed(out i anyelement, inout r anyrange)
+ as $$ select $1, $2 $$ language sql;
+ create function table_succeed(i anyelement, r anyrange) returns table(i anyelement, r anyrange)
+ as $$ select $1, $2 $$ language sql;
+ -- should fail
+ create function outparam_fail(i anyelement, out r anyrange, out t text)
+ as $$ select '[1,10]', 'foo' $$ language sql;
+ ERROR: cannot determine result data type
+ DETAIL: A function returning ANYRANGE must have at least one ANYRANGE argument.
+ --should fail
+ create function inoutparam_fail(inout i anyelement, out r anyrange)
+ as $$ select $1, '[1,10]' $$ language sql;
+ ERROR: cannot determine result data type
+ DETAIL: A function returning ANYRANGE must have at least one ANYRANGE argument.
+ --should fail
+ create function table_succeed(i anyelement) returns table(i anyelement, r anyrange)
+ as $$ select $1, '[1,10]' $$ language sql;
+ ERROR: cannot determine result data type
+ DETAIL: A function returning ANYRANGE must have at least one ANYRANGE argument.
+ -- invoked from plpgsql.sql in order to exercise a specific code path
+ create function rangetypes_plpgsql(out a anyelement, b anyrange, c anyarray)
+ language plpgsql as
+ $$ begin a := upper(b) + c[1]; return; end; $$;
*** 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
*** a/src/test/regress/sql/collate.linux.utf8.sql
--- b/src/test/regress/sql/collate.linux.utf8.sql
***************
*** 385,387 **** DROP COLLATION test0 CASCADE;
--- 385,398 ----
DROP TABLE collate_dep_test1, collate_dep_test4t;
DROP TYPE collate_dep_test2;
+
+ -- test range types and collations
+
+ create type textrange_c as range(subtype=text, collation="C");
+ create type textrange_en_us as range(subtype=text, collation="en_US");
+
+ select textrange_c('A','Z') @> 'b'::text;
+ select textrange_en_us('A','Z') @> 'b'::text;
+
+ drop type textrange_c;
+ drop type textrange_en_us;
*** a/src/test/regress/sql/plpgsql.sql
--- b/src/test/regress/sql/plpgsql.sql
***************
*** 3600,3602 **** select testoa(1,2,1); -- fail at update
--- 3600,3606 ----
drop function arrayassign1();
drop function testoa(x1 int, x2 int, x3 int);
+ -- defined in rangetypes.sql
+ -- test is designed to exercise resolve_polymorphic_argtypes, which is only
+ -- called when invoked from a different backend from which it's defined.
+ select rangetypes_plpgsql(int4range(1,10),ARRAY[2,20]);
*** /dev/null
--- b/src/test/regress/sql/rangetypes.sql
***************
*** 0 ****
--- 1,363 ----
+
+ --
+ -- test parser
+ --
+
+ create type textrange as range (subtype=text, collation="C");
+
+ -- negative tests; should fail
+ select ''::textrange;
+ select '-[a,z)'::textrange;
+ select '[a,z) - '::textrange;
+ select '(",a)'::textrange;
+ select '(,,a)'::textrange;
+ select '(),a)'::textrange;
+ select '(a,))'::textrange;
+ select '(],a)'::textrange;
+ select '(a,])'::textrange;
+
+ -- should succeed
+ select ' empty '::textrange;
+ select ' ( empty, empty ) '::textrange;
+ select ' ( " a " " a ", " z " " z " ) '::textrange;
+ select '(,z)'::textrange;
+ select '(a,)'::textrange;
+ select '[,z]'::textrange;
+ select '[a,]'::textrange;
+ select '( , )'::textrange;
+ select '("","")'::textrange;
+ select '["",""]'::textrange;
+ select '(",",",")'::textrange;
+ select '("\\","\\")'::textrange
+ select '(\\,a)'::textrange;
+ select '((,z)'::textrange;
+ select '([,z)'::textrange;
+ select '(!,()'::textrange;
+ select '(!,[)'::textrange;
+
+ drop type textrange;
+
+ --
+ -- create some test data and test the operators
+ --
+
+ CREATE TABLE numrange_test (nr NUMRANGE);
+ create index numrange_test_btree on numrange_test(nr);
+ SET enable_seqscan = f;
+
+ INSERT INTO numrange_test VALUES('[,)');
+ INSERT INTO numrange_test VALUES('[3,]');
+ INSERT INTO numrange_test VALUES('[, 5)');
+ INSERT INTO numrange_test VALUES(numrange(1.1, 2.2));
+ INSERT INTO numrange_test VALUES('empty');
+ INSERT INTO numrange_test VALUES(numrange(1.7));
+
+ SELECT 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, numrange(1.9,1.91));
+ SELECT * FROM numrange_test WHERE nr @> numrange(1.0,10000.1);
+ SELECT * FROM numrange_test WHERE contained_by(numrange(-1e7,-10000.1), nr);
+ SELECT * FROM numrange_test WHERE 1.9 <@ nr;
+ SELECT * FROM numrange_test WHERE nr = 'empty';
+ SELECT * FROM numrange_test WHERE range_eq(nr, '(1.1, 2.2)');
+ SELECT * FROM numrange_test WHERE nr = '[1.1, 2.2)';
+
+ select numrange(2.0, 1.0);
+
+ select numrange(2.0, 3.0) -|- numrange(3.0, 4.0);
+ select adjacent(numrange(2.0, 3.0), numrange(3.1, 4.0));
+ select numrange(2.0, 3.0, '[]') -|- numrange(3.0, 4.0, '()');
+ select numrange(1.0, 2.0) -|- numrange(2.0, 3.0,'[]');
+ select adjacent(numrange(2.0, 3.0, '(]'), numrange(1.0, 2.0, '(]'));
+
+ select numrange(1.1, 3.3) <@ numrange(0.1,10.1);
+ select numrange(0.1, 10.1) <@ numrange(1.1,3.3);
+
+ select numrange(1.1, 2.2) - numrange(2.0, 3.0);
+ select numrange(1.1, 2.2) - numrange(2.2, 3.0);
+ select numrange(1.1, 2.2,'[]') - numrange(2.0, 3.0);
+ select minus(numrange(10.1,12.2,'[]'), numrange(110.0,120.2,'(]'));
+ select minus(numrange(10.1,12.2,'[]'), numrange(0.0,120.2,'(]'));
+
+ select numrange(4.5, 5.5, '[]') && numrange(5.5, 6.5);
+ select numrange(1.0, 2.0) << numrange(3.0, 4.0);
+ select numrange(1.0, 2.0) >> numrange(3.0, 4.0);
+ select numrange(3.0, 70.0) &< numrange(6.6, 100.0);
+
+ select numrange(1.1, 2.2) < numrange(1.0, 200.2);
+ select numrange(1.1, 2.2) < numrange(1.1, 1.2);
+
+ select numrange(1.0, 2.0) + numrange(2.0, 3.0);
+ select numrange(1.0, 2.0) + numrange(1.5, 3.0);
+ select numrange(1.0, 2.0) + numrange(2.5, 3.0);
+
+ select numrange(1.0, 2.0) * numrange(2.0, 3.0);
+ select numrange(1.0, 2.0) * numrange(1.5, 3.0);
+ select numrange(1.0, 2.0) * numrange(2.5, 3.0);
+
+ select * from numrange_test where nr < numrange(-1000.0, -1000.0,'[]');
+ select * from numrange_test where nr < numrange(0.0, 1.0,'[]');
+ select * from numrange_test where nr < numrange(1000.0, 1001.0,'[]');
+ select * from numrange_test where nr > numrange(-1001.0, -1000.0,'[]');
+ select * from numrange_test where nr > numrange(0.0, 1.0,'[]');
+ select * from numrange_test where nr > numrange(1000.0, 1000.0,'[]');
+
+ create table numrange_test2(nr numrange);
+ create index numrange_test2_hash_idx on numrange_test2 (nr);
+ INSERT INTO numrange_test2 VALUES('[, 5)');
+ INSERT INTO numrange_test2 VALUES(numrange(1.1, 2.2));
+ INSERT INTO numrange_test2 VALUES(numrange(1.1, 2.2));
+ INSERT INTO numrange_test2 VALUES(numrange(1.1, 2.2,'()'));
+ INSERT INTO numrange_test2 VALUES('empty');
+
+ select * from numrange_test2 where nr = 'empty'::numrange;
+ select * from numrange_test2 where nr = numrange(1.1, 2.2);
+ select * from numrange_test2 where nr = numrange(1.1, 2.3);
+
+ set enable_nestloop=t;
+ set enable_hashjoin=f;
+ set enable_mergejoin=f;
+ select * from numrange_test natural join numrange_test2 order by nr;
+ set enable_nestloop=f;
+ set enable_hashjoin=t;
+ set enable_mergejoin=f;
+ select * from numrange_test natural join numrange_test2 order by nr;
+ set enable_nestloop=f;
+ set enable_hashjoin=f;
+ set enable_mergejoin=t;
+ select * from numrange_test natural join numrange_test2 order by nr;
+
+ set enable_nestloop to default;
+ set enable_hashjoin to default;
+ set enable_mergejoin to default;
+ SET enable_seqscan TO DEFAULT;
+ DROP TABLE numrange_test;
+ DROP TABLE numrange_test2;
+
+ -- test canonical form for int4range
+ select int4range(1,10,'[]');
+ select int4range(1,10,'[)');
+ select int4range(1,10,'(]');
+ select int4range(1,10,'[]');
+
+ -- test canonical form for daterange
+ select daterange('2000-01-10'::date, '2000-01-20'::date,'[]');
+ select daterange('2000-01-10'::date, '2000-01-20'::date,'[)');
+ select daterange('2000-01-10'::date, '2000-01-20'::date,'(]');
+ select daterange('2000-01-10'::date, '2000-01-20'::date,'[]');
+
+ create table test_range_gist(ir int4range);
+ create index test_range_gist_idx on test_range_gist using gist (ir);
+
+ insert into test_range_gist select int4range(g, g+10) from generate_series(1,2000) g;
+ insert into test_range_gist select 'empty'::int4range from generate_series(1,500) g;
+ insert into test_range_gist select int4range(g, g+10000) from generate_series(1,1000) g;
+ insert into test_range_gist select 'empty'::int4range from generate_series(1,500) g;
+ insert into test_range_gist select int4range(NULL,g*10,'(]') from generate_series(1,100) g;
+ insert into test_range_gist select int4range(g*10,NULL,'(]') from generate_series(1,100) g;
+ insert into test_range_gist select int4range(g, g+10) from generate_series(1,2000) g;
+
+ BEGIN;
+ SET LOCAL enable_seqscan = t;
+ SET LOCAL enable_bitmapscan = f;
+ SET LOCAL enable_indexscan = f;
+
+ select count(*) from test_range_gist where ir @> 'empty'::int4range;
+ select count(*) from test_range_gist where ir = int4range(10,20);
+ select count(*) from test_range_gist where ir @> 10;
+ select count(*) from test_range_gist where ir @> int4range(10,20);
+ select count(*) from test_range_gist where ir && int4range(10,20);
+ select count(*) from test_range_gist where ir <@ int4range(10,50);
+ select count(*) from (select * from test_range_gist where ir?) s where ir << int4range(100,500);
+ select count(*) from (select * from test_range_gist where ir?) s where ir >> int4range(100,500);
+ select count(*) from (select * from test_range_gist where ir?) s where ir &< int4range(100,500);
+ select count(*) from (select * from test_range_gist where ir?) s where ir &> int4range(100,500);
+ select count(*) from (select * from test_range_gist where ir?) s where ir -|- int4range(100,500);
+ COMMIT;
+
+ BEGIN;
+ SET LOCAL enable_seqscan = f;
+ SET LOCAL enable_bitmapscan = f;
+ SET LOCAL enable_indexscan = t;
+
+ select count(*) from test_range_gist where ir @> 'empty'::int4range;
+ select count(*) from test_range_gist where ir = int4range(10,20);
+ select count(*) from test_range_gist where ir @> 10;
+ select count(*) from test_range_gist where ir @> int4range(10,20);
+ select count(*) from test_range_gist where ir && int4range(10,20);
+ select count(*) from test_range_gist where ir <@ int4range(10,50);
+ select count(*) from test_range_gist where ir << int4range(100,500);
+ select count(*) from test_range_gist where ir >> int4range(100,500);
+ select count(*) from test_range_gist where ir &< int4range(100,500);
+ select count(*) from test_range_gist where ir &> int4range(100,500);
+ select count(*) from test_range_gist where ir -|- int4range(100,500);
+ COMMIT;
+
+ drop index test_range_gist_idx;
+ create index test_range_gist_idx on test_range_gist using gist (ir);
+
+ BEGIN;
+ SET LOCAL enable_seqscan = f;
+ SET LOCAL enable_bitmapscan = f;
+ SET LOCAL enable_indexscan = t;
+
+ select count(*) from test_range_gist where ir @> 'empty'::int4range;
+ select count(*) from test_range_gist where ir = int4range(10,20);
+ select count(*) from test_range_gist where ir @> 10;
+ select count(*) from test_range_gist where ir @> int4range(10,20);
+ select count(*) from test_range_gist where ir && int4range(10,20);
+ select count(*) from test_range_gist where ir <@ int4range(10,50);
+ select count(*) from test_range_gist where ir << int4range(100,500);
+ select count(*) from test_range_gist where ir >> int4range(100,500);
+ select count(*) from test_range_gist where ir &< int4range(100,500);
+ select count(*) from test_range_gist where ir &> int4range(100,500);
+ select count(*) from test_range_gist where ir -|- int4range(100,500);
+ COMMIT;
+
+ drop table test_range_gist;
+
+ --
+ -- Btree_gist is not included by default, so to test exclusion
+ -- constraints with range types, use singleton int ranges for the "="
+ -- portion of the constraint.
+ --
+
+ create table test_range_excl(
+ room int4range,
+ speaker int4range,
+ during tsrange,
+ exclude using gist (room with =, during with &&),
+ exclude using gist (speaker with =, during with &&)
+ );
+
+ insert into test_range_excl
+ values(int4range(123), int4range(1), '[2010-01-02 10:00, 2010-01-02 11:00)');
+ insert into test_range_excl
+ values(int4range(123), int4range(2), '[2010-01-02 11:00, 2010-01-02 12:00)');
+ insert into test_range_excl
+ values(int4range(123), int4range(3), '[2010-01-02 10:10, 2010-01-02 11:10)');
+ insert into test_range_excl
+ values(int4range(124), int4range(3), '[2010-01-02 10:10, 2010-01-02 11:10)');
+ insert into test_range_excl
+ values(int4range(125), int4range(1), '[2010-01-02 10:10, 2010-01-02 11:10)');
+
+ drop table test_range_excl;
+
+ -- test bigint ranges
+ select int8range(10000000000::int8, 20000000000::int8,'(]');
+ -- test tstz ranges
+ set timezone to '-08';
+ select '[2010-01-01 01:00:00 -05, 2010-01-01 02:00:00 -08)'::tstzrange;
+ -- should fail
+ select '[2010-01-01 01:00:00 -08, 2010-01-01 02:00:00 -05)'::tstzrange;
+ set timezone to default;
+
+ --
+ -- Test user-defined range of floats
+ --
+
+ 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(float8range(-100.00007, '1.111113e9'));
+ select * from float8range_test;
+ drop table float8range_test;
+ drop type float8range;
+
+ --
+ -- Test rangetypes over domains
+ --
+
+ create domain mydomain as int4;
+ create type mydomainrange as range(subtype=mydomain);
+ select '[4,50)'::mydomainrange @> 7::mydomain;
+ drop type mydomainrange;
+ drop domain mydomain;
+
+ --
+ -- Test multiple range types over the same subtype
+ --
+
+ create type textrange1 as range(subtype=text, collation="C");
+ create type textrange2 as range(subtype=text, collation="C");
+
+ select textrange1('a','Z') @> 'b'::text;
+ select textrange2('a','z') @> 'b'::text;
+
+ drop type textrange1;
+ drop type textrange2;
+
+ --
+ -- Test out polymorphic type system
+ --
+
+ create function anyarray_anyrange_func(a anyarray, r anyrange)
+ returns anyelement as 'select $1[1] + lower($2);' language sql;
+
+ select anyarray_anyrange_func(ARRAY[1,2], int4range(10,20));
+
+ -- should fail
+ select anyarray_anyrange_func(ARRAY[1,2], numrange(10,20));
+
+ drop function anyarray_anyrange_func(anyarray, anyrange);
+
+ -- should fail
+ create function bogus_func(anyelement)
+ returns anyrange as 'select int4range(1,10)' language sql;
+
+ -- should fail
+ create function bogus_func(int)
+ returns anyrange as 'select int4range(1,10)' language sql;
+
+ create function range_add_bounds(anyrange)
+ returns anyelement as 'select lower($1) + upper($1)' language sql;
+
+ select range_add_bounds(numrange(1.0001, 123.123));
+
+ --
+ -- Arrays of ranges
+ --
+
+ select ARRAY[numrange(1.1), numrange(12.3,155.5)];
+
+ --
+ -- Ranges of arrays
+ --
+
+ create type arrayrange as range (subtype=int4[]);
+
+ select arrayrange(ARRAY[1,2], ARRAY[2,1]);
+
+ drop type arrayrange;
+
+ --
+ -- OUT/INOUT/TABLE functions
+ --
+
+ create function outparam_succeed(i anyrange, out r anyrange, out t text)
+ as $$ select $1, 'foo' $$ language sql;
+
+ create function inoutparam_succeed(out i anyelement, inout r anyrange)
+ as $$ select $1, $2 $$ language sql;
+
+ create function table_succeed(i anyelement, r anyrange) returns table(i anyelement, r anyrange)
+ as $$ select $1, $2 $$ language sql;
+
+ -- should fail
+ create function outparam_fail(i anyelement, out r anyrange, out t text)
+ as $$ select '[1,10]', 'foo' $$ language sql;
+
+ --should fail
+ create function inoutparam_fail(inout i anyelement, out r anyrange)
+ as $$ select $1, '[1,10]' $$ language sql;
+
+ --should fail
+ create function table_succeed(i anyelement) returns table(i anyelement, r anyrange)
+ as $$ select $1, '[1,10]' $$ language sql;
+
+ -- invoked from plpgsql.sql in order to exercise a specific code path
+ create function rangetypes_plpgsql(out a anyelement, b anyrange, c anyarray)
+ language plpgsql as
+ $$ begin a := upper(b) + c[1]; return; end; $$;
*** 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'));