*** a/doc/src/sgml/datatype.sgml --- b/doc/src/sgml/datatype.sgml *************** *** 4190,4195 **** SET xmloption TO { DOCUMENT | CONTENT }; --- 4190,4197 ---- &rowtypes; + &rangetypes; + Object Identifier Types *************** *** 4461,4466 **** SELECT * FROM pg_attribute --- 4463,4472 ---- + anyrange + + + void *************** *** 4537,4542 **** SELECT * FROM pg_attribute --- 4543,4555 ---- + anyrange + Indicates that a function accepts any range data type + (see and + ). + + + anynonarray Indicates that a function accepts any non-array data type (see ). *************** *** 4600,4606 **** SELECT * FROM pg_attribute only void and record as a result type (plus trigger when the function is used as a trigger). Some also support polymorphic functions using the types anyarray, ! anyelement, anyenum, and anynonarray. --- 4613,4620 ---- only void and record as a result type (plus trigger when the function is used as a trigger). Some also support polymorphic functions using the types anyarray, ! anyelement, anyenum, anyrange, and ! anynonarray. *** a/doc/src/sgml/extend.sgml --- b/doc/src/sgml/extend.sgml *************** *** 198,211 **** ! Four pseudo-types of special interest are anyelement, ! anyarray, anynonarray, and anyenum, ! which are collectively called polymorphic types. ! Any function declared using these types is said to be ! a polymorphic function. A polymorphic function can ! operate on many different data types, with the specific data type(s) ! being determined by the data types actually passed to it in a particular ! call. --- 198,212 ---- ! Five pseudo-types of special interest are anyelement, ! anyarray, anynonarray, anyenum, ! and anyrange, which are collectively ! called polymorphic types. Any function declared ! using these types is said to be a polymorphic ! function. A polymorphic function can operate on many ! different data types, with the specific data type(s) being ! determined by the data types actually passed to it in a ! particular call. *************** *** 221,226 **** --- 222,232 ---- anyelement, the actual array type in the anyarray positions must be an array whose elements are the same type appearing in the anyelement positions. + Similarly, if there are positions declared anyrange + and others declared + anyelement, the actual range type in the + anyrange positions must be a range whose subtype is + the same type appearing in the anyelement positions. anynonarray is treated exactly the same as anyelement, but adds the additional constraint that the actual type must not be an array type. *** a/doc/src/sgml/filelist.sgml --- b/doc/src/sgml/filelist.sgml *************** *** 25,30 **** --- 25,31 ---- + *** a/doc/src/sgml/func.sgml --- b/doc/src/sgml/func.sgml *************** *** 10444,10449 **** SELECT NULLIF(value, '(none)') ... --- 10444,10906 ---- + + Range Functions and Operators + + + shows the operators + available for range types. + + + + Range Operators + + + + Operator + Description + Example + Result + + + + + = + equal + range(1,5) = '[1,4]'::int4range + t + + + + <> + not equal + range(1.1,2.2) <> range(1.1,2.3) + t + + + + < + less than + range(1,10) < range(2,3) + t + + + + > + greater than + range(1,10) > range(1,5) + t + + + + <= + less than or equal + range(1.1,2.2) <= range(1.1,2.2) + t + + + + >= + greater than or equal + range(1.1,2.2) >= range(1.1,2.0) + t + + + + @> + contains + '[2011-01-01,2011-03-01)'::tsrange @> '2011-01-10'::timestamp + t + + + + <@ + is contained by + range(2,4) <@ range(1,7) + t + + + + && + overlap (have points in common) + range(3,7) && range(4,12) + t + + + + << + strictly left of + range(1,10) << range(100,110) + t + + + + >> + strictly right of + range(50,60) >> range(20,30) + t + + + + &< + Does not extend to the right of? + range(1,20) &< range(18,20) + t + + + + &> + Does not extend to the left of? + range(7,20) &> range(5,10) + t + + + + -|- + adjacent? + range(1.1,2.2) -|- range(2.2,3.3) + t + + + + + + Union + range(5,15) + range(10,20) + [5,20) + + + + - + Difference + range(5,15) - range(10,20) + [5,10) + + + + * + Intersection + range(5,15) * range(10,20) + [10,15) + + + + !? + Is empty? + '-'::int4range !? + t + + + + ? + Is non-empty? + range(1.0,2.0)? + t + + + +
+ + + Range comparisons compare the lower bounds first, and only if + equal, compare the upper bounds. This is generally most useful for + B-tree indexes, rather than being useful comparisons by themselves. + + + + Array comparisons compare the array contents element-by-element, + using the default B-tree comparison function for the element data type. + In multidimensional arrays the elements are visited in row-major order + (last subscript varies most rapidly). + If the contents of two arrays are equal but the dimensionality is + different, the first difference in the dimensionality information + determines the sort order. (This is a change from versions of + PostgreSQL prior to 8.2: older versions would claim + that two arrays with the same contents were equal, even if the + number of dimensions or subscript ranges were different.) + + + + See for more details about range operator + behavior. + + + + shows the functions + available for use with range types. See + for more information and examples of the use of these functions. + + + + range + + + range__ + + + range_i + + + rangei_ + + + rangeii + + + range_linf_ + + + range_uinf_ + + + range_linfi + + + range_uinfi + + + lower + + + upper + + + empty + + + non_empty + + + lower_inc + + + upper_inc + + + lower_inf + + + upper_inf + + + length + + + + Range Functions + + + + Function + Return Type + Description + Example + Result + + + + + + + range(anyelement, anyelement) + + + anyrange + alias for rangei_ + range(1.1,2.2) + [1.1,2.2) + + + + + range__(anyelement, anyelement) + + + anyrange + construct a range with exclusive lower bound and exclusive upper bound + range__(1.1,2.2) + (1.1,2.2) + + + + + range_i(anyelement, anyelement) + + + anyrange + construct a range with exclusive lower bound and inclusive upper bound + range_i(1.1,2.2) + (1.1,2.2] + + + + + rangei_(anyelement, anyelement) + + + anyrange + construct a range with inclusive lower bound and exclusive upper bound + rangei_(1.1,2.2) + [1.1,2.2) + + + + + rangeii(anyelement, anyelement) + + + anyrange + construct a range with inclusive lower bound and inclusive upper bound + rangeii(1.1,2.2) + [1.1,2.2] + + + + + range_linf_(anyelement) + + + anyrange + construct a range with infinite lower bound and exclusive upper bound + range_linf_(1.1) + (-INF,1.1) + + + + + range_uinf_(anyelement) + + + anyrange + construct a range with exclusive lower bound and infinite upper bound + range_uinf_(1.1) + (1.1,INF) + + + + + range_linfi(anyelement) + + + anyrange + construct a range with infinite lower bound and inclusive upper bound + range_linfi(1.1) + (-INF,1.1] + + + + + range_uinfi(anyelement) + + + anyrange + construct a range with inclusive lower bound and infinite upper bound + range_uinfi(1.1) + [1.1,INF) + + + + + lower(anyrange) + + + anyrange + lower bound of range + lower(range(1.1,2.2)) + 1.1 + + + + + upper(anyrange) + + + anyrange + upper bound of range + upper(range(1.1,2.2)) + 2.2 + + + + + empty(anyrange) + + + anyrange + is the range empty? + empty(range(1.1,2.2)) + false + + + + + non_empty(anyrange) + + + anyrange + is the range non-empty? + non_empty(range(1.1,2.2)) + true + + + + + lower_inc(anyrange) + + + anyrange + is the lower bound of the range inclusive? + lower_inc(range(1.1,2.2)) + true + + + + + upper_inc(anyrange) + + + anyrange + is the upper bound of the range inclusive? + upper_inc(range(1.1,2.2)) + false + + + + + lower_inf(anyrange) + + + anyrange + is the lower bound of the range infinite? + lower_inf('(-INF,INF)'::daterange) + true + + + + + upper_inf(anyrange) + + + anyrange + is the upper bound of the range infinite? + upper_inf('(-INF,INF)'::daterange) + true + + + + + length(anyrange) + + + anyelement + what is the upper bound of the range minus the lower bound? + length(range(10.1,20.1)) + 10.0 + + + +
+
+ Aggregate Functions *** a/doc/src/sgml/plpgsql.sgml --- b/doc/src/sgml/plpgsql.sgml *************** *** 139,145 **** PL/pgSQL functions can also be declared to accept and return the polymorphic types anyelement, anyarray, anynonarray, ! and anyenum. The actual data types handled by a polymorphic function can vary from call to call, as discussed in . An example is shown in . --- 139,145 ---- PL/pgSQL functions can also be declared to accept and return the polymorphic types anyelement, anyarray, anynonarray, ! anyenum, and anyrange. The actual data types handled by a polymorphic function can vary from call to call, as discussed in . An example is shown in . *************** *** 500,507 **** $$ LANGUAGE plpgsql; When the return type of a PL/pgSQL function is declared as a polymorphic type (anyelement, ! anyarray, anynonarray, or anyenum), ! a special parameter $0 is created. Its data type is the actual return type of the function, as deduced from the actual input types (see ). --- 500,507 ---- When the return type of a PL/pgSQL function is declared as a polymorphic type (anyelement, ! anyarray, anynonarray, anyenum, ! or anyrange), a special parameter $0 is created. Its data type is the actual return type of the function, as deduced from the actual input types (see ). *** /dev/null --- b/doc/src/sgml/rangetypes.sgml *************** *** 0 **** --- 1,328 ---- + + + + Range Types + + + range type + + + + Range types are data types representing a range of values over some + sub-type with a total order. For instance, ranges + of timestamp might be used to represent the ranges of + time that a meeting room is reserved. In this case the data type + is tsrange (short for "timestamp range"), + and timestamp is the sub-type with a total order. + + + + Range types are useful because they represent many points in a + single value. The use of time and date ranges for scheduling + purposes is the clearest example; but price ranges, measurement + ranges from an instrument, etc., are also useful. + + + + Built-in Range Types + + Built-in range types: + + + + INT4RANGE -- Range of INTEGER. This is a discrete range type, see . + + + + + INT8RANGE -- Range of BIGINT. This is a discrete range type, see . + + + + + NUMRANGE -- Range of NUMERIC. + + + + + TSRANGE -- Range of TIMESTAMP WITHOUT TIME ZONE. + + + + + TSTZRANGE -- Range of TIMESTAMP WITH TIME ZONE. + + + + + DATERANGE -- Range of DATE. This is a discrete range type, see . + + + + + + + + Examples + + + CREATE TABLE reservation ( during TSRANGE ); + INSERT INTO reservation VALUES + ( '[2010-01-01 14:30, 2010-01-01 15:30)' ); + + -- Containment + SELECT range(10, 20) @> 3; + + -- Overlaps + SELECT range(11.1, 22.2) && range(20.0, 30.0) + + -- Find the upper bound: + SELECT upper(range(15, 25)); + + -- Compute the intersection: + SELECT range(10, 20) * range(15, 25); + + -- Is the range non-empty? + SELECT range(1, 5)? ; + + + + See + and for complete lists of + functions and operators on range types. + + + + + Inclusive and Exclusive Bounds + + Every range has two bounds, the lower bound and the upper bound. All + points in between those values are included in the range. An + inclusive bound means that the boundary point itself is included in + the range as well, while an exclusive bound means that the boundary + point is not included in the range. + + + An inclusive lower bound is represented by "[" + while an exclusive lower bound is represented + by "(". Likewise, an inclusive upper bound is + represented by "]", while an exclusive upper bound + is represented by ")". + + + Functions lower_inc + and upper_inc test the inclusivity of the lower + and upper bounds of a range, respectively. + + + + + Infinite (unbounded) Ranges + + The lower bound of a range can be -INF, meaning + that all points less than the upper bound are included in the + range. Likewise, if the upper bound of the range + is INF, then all points greater than the lower + bound are included in the range. If both lower and upper bounds are + infinite, all points are considered to be in the range. + + + Functions lower_inf + and upper_inf test the range for infinite lower + and upper bounds of a range, respectively. + + + + + Input/Output + + The input follows one of the following patterns: + + ( lower-bound , upper-bound ) + ( lower-bound , upper-bound ] + [ lower-bound , upper-bound ) + [ lower-bound , upper-bound ] + - + + Notice that the final pattern is a "-", which + represents an empty range (a range that contains no points). + + + The lower-bound may be either a string + that is valid input for the sub-type, or -INF; + and upper-bound may be either a string + that is valid input for the sub-type, or INF. + + + Either the lower-bound or + the upper-bound may be quoted + using "..." (double quotation), which will + preserve internal whitespace and special characters such as + ",". Within quotation marks, + "\" (backslash) serves as an escape character. + + + The choice between the other input formats affects the inclusivity + of the bounds. See . + + + + + Constructing Ranges + + Some examples of constructing ranges from existing values: + + SELECT range_i(1.0, 14.0); + + -- the int4range result will appear in the canonical form; see + SELECT range_i(1, 14); + + SELECT range_uinfi(11.1); + + SELECT range_linf_(2.2); + + + + The function names follow a pattern where the suffix + "_" is used to mean "exclusive" and + "i" is used to mean "inclusive". In the case of a + two-character suffix, the first character refers to the lower bound, + and the second refers to the upper bound. The + function range assumes an inclusive lower bound + and an exclusive upper bound; and is an alias + for rangei_. + + + For a complete set of constructors, see . + + + + + Discrete Range Types + + Discrete ranges are those that have a + defined canonical function. Loosely speaking, a + discrete range has a sub-type with a well-defined "step"; + e.g. INTEGER or DATE. + + + The canonical function should take an input range + value, and return an equal range value that may have a different + formatting. For instance, the integer range [1, + 7] could be represented by the equal integer + range [1, 8). The two values are equal because + there are no points within the integer domain + between 7 and 8, so not + including the end point 8 is the same as + including the end point 7. + + + For types such as NUMRANGE, this is not possible, + because there are always points in between two + distinct NUMERIC values. + + + + + Defining New Range Types + + To define a new range type of sub-type DOUBLE PRECISION: + + CREATE TYPE FLOATRANGE AS RANGE ( + SUBTYPE = DOUBLE PRECISION, + SUBTYPE_CMP = btfloat8cmp + ); + + SELECT '[1.234, 5.678]'::floatrange; + + Because DOUBLE PRECISION has no meaningful "step", we + do not define a canonical + function. See for more + information. + + + + + + range type + gist + + GiST Indexing + + GiST indexes can be applied to a table containing a range type. For instance: + + CREATE INDEX reservation_idx ON reservation USING gist (during); + + This index may speed up queries + involving && + (overlaps), @> (contains), and many other + operators found in this + table: . + + + + + + range type + exclude + + Constraints on Ranges + + While UNIQUE is a natural constraint for scalar + values, it is usually unsuitable for range types. Instead, an + exclusion constraint is often more appropriate + (see CREATE TABLE + ... CONSTRAINT ... EXCLUDE). Exclusion constraints allow the + specification of constraints such as "non-overlapping" on a range + type. For example: + + ALTER TABLE reservation + ADD EXCLUDE USING gist (during WITH &&); + + That constraint will prevent any overlapping values from existing + in the table at the same time: + + INSERT INTO reservation VALUES + ( '[2010-01-01 11:30, 2010-01-01 13:00)' ); + -- Result: INSERT 0 1 + INSERT INTO reservation VALUES + ( '[2010-01-01 14:45, 2010-01-01 15:45)' ); + -- Result: + -- ERROR: conflicting key value violates exclusion constraint "reservation_during_excl" + -- DETAIL: Key (during)=([ 2010-01-01 14:45:00, 2010-01-01 15:45:00 )) conflicts with + -- existing key (during)=([ 2010-01-01 14:30:00, 2010-01-01 15:30:00 )). + + + + Combine range types and exclusion constraints + with btree_gist for maximum + flexibility defining + constraints. After btree_gist is installed, the + following constraint will prevent overlapping ranges only if the + meeting room numbers are equal: + + + CREATE TABLE room_reservation + ( + room TEXT, + during TSRANGE, + EXCLUDE USING gist (room WITH =, during WITH &&) + ); + + INSERT INTO room_reservation VALUES + ( '123A', '[2010-01-01 14:00, 2010-01-01 15:00)' ); + -- Result: INSERT 0 1 + INSERT INTO room_reservation VALUES + ( '123A', '[2010-01-01 14:30, 2010-01-01 15:30)' ); + -- Result: + -- ERROR: conflicting key value violates exclusion constraint "room_reservation_room_during_excl" + -- DETAIL: Key (room, during)=(123A, [ 2010-01-01 14:30:00, 2010-01-01 15:30:00 )) conflicts with + -- existing key (room, during)=(123A, [ 2010-01-01 14:00:00, 2010-01-01 15:00:00 )). + INSERT INTO room_reservation VALUES + ( '123B', '[2010-01-01 14:30, 2010-01-01 15:30)' ); + -- Result: INSERT 0 1 + + + + + *** a/doc/src/sgml/ref/create_type.sgml --- b/doc/src/sgml/ref/create_type.sgml *************** *** 27,32 **** CREATE TYPE name AS --- 27,40 ---- CREATE TYPE name AS ENUM ( [ 'label' [, ... ] ] ) + CREATE TYPE name AS RANGE ( + SUBTYPE = subtype, + SUBTYPE_CMP = subtype_cmp_function + [ , SUBTYPE_FLOAT = subtype_float_function ] + [ , CANONICAL = canonical_function ] + [ , ANALYZE = analyze_function ] + ) + CREATE TYPE name ( INPUT = input_function, OUTPUT = output_function *************** *** 98,108 **** CREATE TYPE name Base Types ! The third form of CREATE TYPE creates a new base type (scalar type). To create a new base type, you must be a superuser. (This restriction is made because an erroneous type definition could confuse or even crash the server.) --- 106,165 ---- + + Range Types + + + The third form of CREATE TYPE creates a new + range type, as described in . To create + a new range type, you must be a superuser. + + + + The subtype parameter + can be any type. The + function subtype_cmp + must take two arguments of + type subtype, and + compare them in a total order, + returning -1, 0, + or 1 when the first argument is less than, + equal to, or greater than the second argument, respectively. + + + + The subtype_float + function takes a value of + type subtype and + returns a value of type double precision. This + function is used for GiST indexing (see for + more information), and should be provided for efficiency. + + + + The canonical + function takes an argument and returns a value, both of the same + type being defined. This is used to convert the range value to a + canonical form when applicable. See + for more information. To define + a canonical function, + you must first create a shell type, which is a + placeholder type that has no properties except a name and an + owner. This is done by issuing the command CREATE TYPE + name, with no additional parameters. + + + + The analyze + function is the same as for creating a base type. + + + Base Types ! The fourth form of CREATE TYPE creates a new base type (scalar type). To create a new base type, you must be a superuser. (This restriction is made because an erroneous type definition could confuse or even crash the server.) *** a/doc/src/sgml/xfunc.sgml --- b/doc/src/sgml/xfunc.sgml *************** *** 997,1004 **** $$ LANGUAGE SQL; SQL functions can be declared to accept and return the polymorphic types anyelement, ! anyarray, anynonarray, and ! anyenum. See for a more detailed explanation of polymorphic functions. Here is a polymorphic function make_array that builds up an array --- 997,1004 ---- SQL functions can be declared to accept and return the polymorphic types anyelement, ! anyarray, anynonarray, ! anyenum, and anyrange. See for a more detailed explanation of polymorphic functions. Here is a polymorphic function make_array that builds up an array *************** *** 3046,3052 **** CREATE OR REPLACE FUNCTION retcomposite(IN integer, IN integer, C-language functions can be declared to accept and return the polymorphic types anyelement, anyarray, anynonarray, ! and anyenum. See for a more detailed explanation of polymorphic functions. When function arguments or return types are defined as polymorphic types, the function author cannot know --- 3046,3052 ---- C-language functions can be declared to accept and return the polymorphic types anyelement, anyarray, anynonarray, ! anyenum, and anyrange. See for a more detailed explanation of polymorphic functions. When function arguments or return types are defined as polymorphic types, the function author cannot know *** a/src/backend/catalog/Makefile --- b/src/backend/catalog/Makefile *************** *** 13,20 **** include $(top_builddir)/src/Makefile.global OBJS = catalog.o dependency.o heap.o index.o indexing.o namespace.o aclchk.o \ objectaddress.o pg_aggregate.o pg_collation.o pg_constraint.o pg_conversion.o \ pg_depend.o pg_enum.o pg_inherits.o pg_largeobject.o pg_namespace.o \ ! pg_operator.o pg_proc.o pg_db_role_setting.o pg_shdepend.o pg_type.o \ ! storage.o toasting.o BKIFILES = postgres.bki postgres.description postgres.shdescription --- 13,20 ---- OBJS = catalog.o dependency.o heap.o index.o indexing.o namespace.o aclchk.o \ objectaddress.o pg_aggregate.o pg_collation.o pg_constraint.o pg_conversion.o \ pg_depend.o pg_enum.o pg_inherits.o pg_largeobject.o pg_namespace.o \ ! pg_operator.o pg_proc.o pg_range.o pg_db_role_setting.o pg_shdepend.o \ ! pg_type.o storage.o toasting.o BKIFILES = postgres.bki postgres.description postgres.shdescription *************** *** 39,45 **** POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\ pg_ts_parser.h pg_ts_template.h pg_extension.h \ pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \ pg_foreign_table.h \ ! pg_default_acl.h pg_seclabel.h pg_shseclabel.h pg_collation.h \ toasting.h indexing.h \ ) --- 39,45 ---- pg_ts_parser.h pg_ts_template.h pg_extension.h \ pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \ pg_foreign_table.h \ ! pg_default_acl.h pg_seclabel.h pg_shseclabel.h pg_collation.h pg_range.h \ toasting.h indexing.h \ ) *** a/src/backend/catalog/pg_proc.c --- b/src/backend/catalog/pg_proc.c *************** *** 166,171 **** ProcedureCreate(const char *procedureName, --- 166,172 ---- case ANYELEMENTOID: case ANYNONARRAYOID: case ANYENUMOID: + case ANYRANGEOID: genericInParam = true; break; case INTERNALOID: *************** *** 190,195 **** ProcedureCreate(const char *procedureName, --- 191,197 ---- case ANYELEMENTOID: case ANYNONARRAYOID: case ANYENUMOID: + case ANYRANGEOID: genericOutParam = true; break; case INTERNALOID: *** /dev/null --- b/src/backend/catalog/pg_range.c *************** *** 0 **** --- 1,135 ---- + /*------------------------------------------------------------------------- + * + * pg_range.c + * routines to support manipulation of the pg_range relation + * + * Copyright (c) 2006-2010, PostgreSQL Global Development Group + * + * + * IDENTIFICATION + * src/backend/catalog/pg_range.c + * + *------------------------------------------------------------------------- + */ + #include "postgres.h" + + #include "access/genam.h" + #include "access/heapam.h" + #include "catalog/dependency.h" + #include "catalog/indexing.h" + #include "catalog/pg_collation.h" + #include "catalog/pg_opclass.h" + #include "catalog/pg_proc.h" + #include "catalog/pg_range.h" + #include "catalog/pg_type.h" + #include "utils/fmgroids.h" + #include "utils/tqual.h" + #include "utils/rel.h" + + /* + * RangeCreate + * Create an entry in pg_range. + */ + void + RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation, + regproc rangeSubOpclass, regproc rangeCanonical, + regproc rangeSubFloat) + { + Relation pg_range; + Datum values[Natts_pg_range]; + bool nulls[Natts_pg_range]; + HeapTuple tup; + ObjectAddress myself; + ObjectAddress referenced; + + pg_range = heap_open(RangeRelationId, RowExclusiveLock); + + memset(nulls, 0, Natts_pg_range * sizeof(bool)); + + values[Anum_pg_range_rngtypid - 1] = ObjectIdGetDatum(rangeTypeOid); + values[Anum_pg_range_rngsubtype - 1] = ObjectIdGetDatum(rangeSubType); + values[Anum_pg_range_rngcollation - 1] = ObjectIdGetDatum(rangeCollation); + values[Anum_pg_range_rngsubopc - 1] = ObjectIdGetDatum(rangeSubOpclass); + values[Anum_pg_range_rngcanonical - 1] = ObjectIdGetDatum(rangeCanonical); + values[Anum_pg_range_rngsubfloat - 1] = ObjectIdGetDatum(rangeSubFloat); + + tup = heap_form_tuple(RelationGetDescr(pg_range), values, nulls); + simple_heap_insert(pg_range, tup); + CatalogUpdateIndexes(pg_range, tup); + heap_freetuple(tup); + + /* record dependencies */ + + myself.classId = TypeRelationId; + myself.objectId = rangeTypeOid; + myself.objectSubId = 0; + + referenced.classId = TypeRelationId; + referenced.objectId = rangeSubType; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + + referenced.classId = OperatorClassRelationId; + referenced.objectId = rangeSubOpclass; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + + if (OidIsValid(rangeCollation)) + { + referenced.classId = CollationRelationId; + referenced.objectId = rangeCollation; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + } + + if (OidIsValid(rangeCanonical)) + { + referenced.classId = ProcedureRelationId; + referenced.objectId = rangeCanonical; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + } + + if (OidIsValid(rangeSubFloat)) + { + referenced.classId = ProcedureRelationId; + referenced.objectId = rangeSubFloat; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + } + + heap_close(pg_range, RowExclusiveLock); + } + + + /* + * RangeDelete + * Remove the pg_range entry. + */ + void + RangeDelete(Oid rangeTypeOid) + { + Relation pg_range; + ScanKeyData key[1]; + SysScanDesc scan; + HeapTuple tup; + + pg_range = heap_open(RangeRelationId, RowExclusiveLock); + + ScanKeyInit(&key[0], + Anum_pg_range_rngtypid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(rangeTypeOid)); + + scan = systable_beginscan(pg_range, RangeTypidIndexId, true, + SnapshotNow, 1, key); + + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + simple_heap_delete(pg_range, &tup->t_self); + } + + systable_endscan(scan); + + heap_close(pg_range, RowExclusiveLock); + } *** a/src/backend/commands/typecmds.c --- b/src/backend/commands/typecmds.c *************** *** 43,48 **** --- 43,50 ---- #include "catalog/pg_depend.h" #include "catalog/pg_enum.h" #include "catalog/pg_namespace.h" + #include "catalog/pg_proc.h" + #include "catalog/pg_range.h" #include "catalog/pg_type.h" #include "catalog/pg_type_fn.h" #include "commands/defrem.h" *************** *** 87,92 **** static Oid findTypeSendFunction(List *procname, Oid typeOid); --- 89,97 ---- static Oid findTypeTypmodinFunction(List *procname); static Oid findTypeTypmodoutFunction(List *procname); static Oid findTypeAnalyzeFunction(List *procname, Oid typeOid); + static Oid findRangeCanonicalFunction(List *procname, Oid typeOid); + static Oid findRangeSubOpclass(List *procname, Oid typeOid); + static Oid findRangeSubtypeFloatFunction(List *procname, Oid typeOid); static void validateDomainConstraint(Oid domainoid, char *ccbin); static List *get_rels_with_domain(Oid domainOid, LOCKMODE lockmode); static void checkDomainOwner(HeapTuple tup); *************** *** 95,100 **** static char *domainAddConstraint(Oid domainOid, Oid domainNamespace, --- 100,106 ---- Oid baseTypeOid, int typMod, Constraint *constr, char *domainName); + static HeapTuple OpClassCacheLookup(Oid amID, List *opclassname, bool missing_ok); /* *************** *** 735,740 **** RemoveTypeById(Oid typeOid) --- 741,754 ---- if (((Form_pg_type) GETSTRUCT(tup))->typtype == TYPTYPE_ENUM) EnumValuesDelete(typeOid); + /* + * If it is a range type, delete the pg_range entries too; we + * don't bother with making dependency entries for those, so it + * has to be done "by hand" here. + */ + if (((Form_pg_type) GETSTRUCT(tup))->typtype == TYPTYPE_RANGE) + RangeDelete(typeOid); + ReleaseSysCache(tup); heap_close(relation, RowExclusiveLock); *************** *** 1227,1232 **** DefineEnum(CreateEnumStmt *stmt) --- 1241,1513 ---- } /* + * DefineRange + * Registers a new range type. + */ + void + DefineRange(CreateRangeStmt *stmt) + { + char *typeName; + char *rangeArrayName; + Oid typeNamespace; + Oid typoid; + Oid rangeArrayOid; + List *parameters = stmt->params; + + ListCell *lc; + List *rangeSubOpclassName = NIL; + List *rangeSubtypeFloatName = NIL; + List *rangeCollationName = NIL; + Oid rangeCollation = InvalidOid; + regproc rangeAnalyze = InvalidOid; + Oid rangeSubtype = InvalidOid; + regproc rangeSubOpclass = InvalidOid; + regproc rangeCanonical = InvalidOid; + regproc rangeSubtypeFloat = InvalidOid; + + + /* + * As of Postgres 8.4, we require superuser privilege to create a base + * type. This is simple paranoia: there are too many ways to mess up the + * system with an incorrect type definition (for instance, representation + * parameters that don't match what the C code expects). In practice it + * takes superuser privilege to create the I/O functions, and so the + * former requirement that you own the I/O functions pretty much forced + * superuserness anyway. We're just making doubly sure here. + * + * XXX re-enable NOT_USED code sections below if you remove this test. + */ + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to create a range type"))); + + /* Convert list of names to a name and namespace */ + typeNamespace = QualifiedNameGetCreationNamespace(stmt->typeName, + &typeName); + + #ifdef NOT_USED + /* XXX this is unnecessary given the superuser check above */ + /* Check we have creation rights in target namespace */ + aclresult = pg_namespace_aclcheck(typeNamespace, GetUserId(), ACL_CREATE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(typeNamespace)); + #endif + + /* + * Look to see if type already exists (presumably as a shell; if not, + * TypeCreate will complain). + */ + typoid = GetSysCacheOid2(TYPENAMENSP, + CStringGetDatum(typeName), + ObjectIdGetDatum(typeNamespace)); + + /* + * If it's not a shell, see if it's an autogenerated array type, and if so + * rename it out of the way. + */ + if (OidIsValid(typoid) && get_typisdefined(typoid)) + { + if (moveArrayTypeName(typoid, typeName, typeNamespace)) + typoid = InvalidOid; + } + + /* + * If it doesn't exist, create it as a shell, so that the OID is known for + * use in the I/O function definitions. + */ + if (!OidIsValid(typoid)) + { + typoid = TypeShellMake(typeName, typeNamespace, GetUserId()); + /* Make new shell type visible for modification below */ + CommandCounterIncrement(); + + /* + * If the command was a parameterless CREATE TYPE, we're done --- + * creating the shell type was all we're supposed to do. + */ + if (parameters == NIL) + return; + } + else + { + /* Complain if dummy CREATE TYPE and entry already exists */ + if (parameters == NIL) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("type \"%s\" already exists", typeName))); + } + + /* TODO: parse parameters */ + foreach(lc, stmt->params) + { + DefElem *defel = lfirst(lc); + + if (pg_strcasecmp(defel->defname, "subtype") == 0) + { + if (OidIsValid(rangeSubtype)) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + rangeSubtype = typenameTypeId(NULL, defGetTypeName(defel)); + } + else if (pg_strcasecmp(defel->defname, "canonical") == 0) + { + if (OidIsValid(rangeCanonical)) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + rangeCanonical = findRangeCanonicalFunction( + defGetQualifiedName(defel), typoid); + } + else if (pg_strcasecmp(defel->defname, "collation") == 0) + { + if (rangeCollationName != NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + rangeCollationName = defGetQualifiedName(defel); + } + else if (pg_strcasecmp(defel->defname, "analyze") == 0) + { + if (OidIsValid(rangeAnalyze)) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + rangeAnalyze = findTypeAnalyzeFunction(defGetQualifiedName(defel), + typoid); + } + else if (pg_strcasecmp(defel->defname, "subtype_opclass") == 0) + { + if (rangeSubOpclassName != NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + rangeSubOpclassName = defGetQualifiedName(defel); + } + else if (pg_strcasecmp(defel->defname, "subtype_float") == 0) + { + if (rangeSubtypeFloatName != NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + rangeSubtypeFloatName = defGetQualifiedName(defel); + } + else + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("type attribute \"%s\" not recognized", + defel->defname))); + continue; + } + } + + if (!OidIsValid(rangeSubtype)) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("type attribute \"subtype\" is required"))); + + if (type_is_collatable(rangeSubtype)) + { + if (rangeCollationName == NIL) + rangeCollation = get_typcollation(rangeSubtype); + else + rangeCollation = get_collation_oid(rangeCollationName, false); + } + else if (rangeCollationName != NIL) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("range collation provided but subtype does not support collation"))); + + rangeSubOpclass = findRangeSubOpclass(rangeSubOpclassName, rangeSubtype); + + if (rangeSubtypeFloatName != NIL) + rangeSubtypeFloat = findRangeSubtypeFloatFunction( + rangeSubtypeFloatName, rangeSubtype); + + rangeArrayOid = AssignTypeArrayOid(); + + /* Create the pg_type entry */ + typoid = + TypeCreate(InvalidOid, /* no predetermined type OID */ + typeName, /* type name */ + typeNamespace, /* namespace */ + InvalidOid, /* relation oid (n/a here) */ + 0, /* relation kind (ditto) */ + GetUserId(), /* owner's ID */ + -1, /* internal size */ + TYPTYPE_RANGE, /* type-type (range type) */ + TYPCATEGORY_RANGE, /* type-category (range type) */ + false, /* range types are never preferred */ + DEFAULT_TYPDELIM, /* array element delimiter */ + F_RANGE_IN, /* input procedure */ + F_RANGE_OUT, /* output procedure */ + F_RANGE_RECV, /* receive procedure */ + F_RANGE_SEND, /* send procedure */ + InvalidOid, /* typmodin procedure - none */ + InvalidOid, /* typmodout procedure - none */ + rangeAnalyze, /* analyze procedure - default */ + InvalidOid, /* element type ID */ + false, /* this is not an array type */ + rangeArrayOid, /* array type we are about to create */ + InvalidOid, /* base type ID (only for domains) */ + NULL, /* never a default type value */ + NULL, /* binary default isn't sent either */ + false, /* never passed by value */ + 'i', /* int alignment */ + 'x', /* TOAST strategy always plain */ + -1, /* typMod (Domains only) */ + 0, /* Array dimensions of typbasetype */ + false, /* Type NOT NULL */ + InvalidOid); /* typcollation */ + + /* create the entry in pg_range */ + RangeCreate(typoid, rangeSubtype, rangeCollation, rangeSubOpclass, + rangeCanonical, rangeSubtypeFloat); + + /* + * Create the array type that goes with it. + */ + rangeArrayName = makeArrayTypeName(typeName, typeNamespace); + + TypeCreate(rangeArrayOid, /* force assignment of this type OID */ + rangeArrayName, /* type name */ + typeNamespace, /* namespace */ + InvalidOid, /* relation oid (n/a here) */ + 0, /* relation kind (ditto) */ + GetUserId(), /* owner's ID */ + -1, /* internal size (always varlena) */ + TYPTYPE_BASE, /* type-type (base type) */ + TYPCATEGORY_ARRAY, /* type-category (array) */ + false, /* array types are never preferred */ + DEFAULT_TYPDELIM, /* array element delimiter */ + F_ARRAY_IN, /* input procedure */ + F_ARRAY_OUT, /* output procedure */ + F_ARRAY_RECV, /* receive procedure */ + F_ARRAY_SEND, /* send procedure */ + InvalidOid, /* typmodin procedure - none */ + InvalidOid, /* typmodout procedure - none */ + InvalidOid, /* analyze procedure - default */ + typoid, /* element type ID */ + true, /* yes this is an array type */ + InvalidOid, /* no further array type */ + InvalidOid, /* base type ID */ + NULL, /* never a default type value */ + NULL, /* binary default isn't sent either */ + false, /* never passed by value */ + 'i', /* align 'i' */ + 'x', /* ARRAY is always toastable */ + -1, /* typMod (Domains only) */ + 0, /* Array dimensions of typbasetype */ + false, /* Type NOT NULL */ + InvalidOid); /* typcollation */ + + pfree(rangeArrayName); + } + + /* * AlterEnum * Adds a new label to an existing enum. */ *************** *** 1542,1547 **** findTypeAnalyzeFunction(List *procname, Oid typeOid) --- 1823,1943 ---- } /* + * Find named btree opclass for subtype, or default btree opclass if + * opcname is NIL. This will be used for comparing values of subtype. + */ + static Oid + findRangeSubOpclass(List *opcname, Oid subtype) + { + Oid opcid; + HeapTuple opctuple; + + if (opcname == NIL) + { + opcid = GetDefaultOpClass(subtype, BTREE_AM_OID); + if (!OidIsValid(opcid)) + { + HeapTuple amtuple; + Form_pg_am pg_am; + amtuple = SearchSysCache1(AMOID, + ObjectIdGetDatum(BTREE_AM_OID)); + if (!HeapTupleIsValid(amtuple)) + elog(ERROR, "cache lookup failed for access method %u", + BTREE_AM_OID); + pg_am = (Form_pg_am) GETSTRUCT(amtuple); + + /* + * No need to call ReleaseSysCache because this is only an + * error path. + */ + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("data type %s has no default operator class for access method \"%s\"", + format_type_be(subtype), NameStr(pg_am->amname)), + errhint("You must specify an operator class for the data type or define a default operator class for the data type."))); + } + return opcid; + } + + + opctuple = OpClassCacheLookup(BTREE_AM_OID, opcname, false); + + opcid = HeapTupleGetOid(opctuple); + + ReleaseSysCache(opctuple); + + return opcid; + } + + /* + * Used to find a range's 'canonical' function. + */ + static Oid + findRangeSubtypeFloatFunction(List *procname, Oid typeOid) + { + Oid argList[1]; + Oid procOid; + + argList[0] = typeOid; + + procOid = LookupFuncName(procname, 1, argList, true); + + if (!OidIsValid(procOid)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_FUNCTION), + errmsg("function %s does not exist", + func_signature_string(procname, 1, NIL, argList)))); + + if (get_func_rettype(procOid) != FLOAT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("range subtype float function %s must return type \"float8\"", + func_signature_string(procname, 1, NIL, argList)))); + + if (func_volatile(procOid) != PROVOLATILE_IMMUTABLE) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("range subtype float function %s must be immutable", + func_signature_string(procname, 1, NIL, argList)))); + + return procOid; + } + + /* + * Used to find a range's 'canonical' function. + */ + static Oid + findRangeCanonicalFunction(List *procname, Oid typeOid) + { + Oid argList[1]; + Oid procOid; + + argList[0] = typeOid; + + procOid = LookupFuncName(procname, 1, argList, true); + + if (!OidIsValid(procOid)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_FUNCTION), + errmsg("function %s does not exist", + func_signature_string(procname, 1, NIL, argList)))); + + if (get_func_rettype(procOid) != typeOid) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("range canonical function %s must return range type", + NameListToString(procname)))); + + if (func_volatile(procOid) != PROVOLATILE_IMMUTABLE) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("range canonical function %s must be immutable", + func_signature_string(procname, 1, NIL, argList)))); + + return procOid; + } + + /* * AssignTypeArrayOid * * Pre-assign the type's array OID for use in pg_type.typarray *************** *** 3072,3074 **** AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid, --- 3468,3526 ---- return oldNspOid; } + + + /* + * OpClassCacheLookup + * Look up an existing opclass by name. + * + * Returns a syscache tuple reference, or NULL if not found. + */ + static HeapTuple + OpClassCacheLookup(Oid amID, List *opclassname, bool missing_ok) + { + char *schemaname; + char *opcname; + HeapTuple htup; + + /* deconstruct the name list */ + DeconstructQualifiedName(opclassname, &schemaname, &opcname); + + if (schemaname) + { + /* Look in specific schema only */ + Oid namespaceId; + + namespaceId = LookupExplicitNamespace(schemaname); + htup = SearchSysCache3(CLAAMNAMENSP, + ObjectIdGetDatum(amID), + PointerGetDatum(opcname), + ObjectIdGetDatum(namespaceId)); + } + else + { + /* Unqualified opclass name, so search the search path */ + Oid opcID = OpclassnameGetOpcid(amID, opcname); + + if (!OidIsValid(opcID)) + htup = NULL; + else + htup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opcID)); + } + + if (!HeapTupleIsValid(htup) && !missing_ok) + { + HeapTuple amtup; + + amtup = SearchSysCache1(AMOID, ObjectIdGetDatum(amID)); + if (!HeapTupleIsValid(amtup)) + elog(ERROR, "cache lookup failed for access method %u", amID); + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("operator class \"%s\" does not exist for access method \"%s\"", + NameListToString(opclassname), + NameStr(((Form_pg_am) GETSTRUCT(amtup))->amname)))); + } + + return htup; + } *** a/src/backend/nodes/copyfuncs.c --- b/src/backend/nodes/copyfuncs.c *************** *** 3034,3039 **** _copyCreateEnumStmt(CreateEnumStmt *from) --- 3034,3050 ---- return newnode; } + static CreateRangeStmt * + _copyCreateRangeStmt(CreateRangeStmt *from) + { + CreateRangeStmt *newnode = makeNode(CreateRangeStmt); + + COPY_NODE_FIELD(typeName); + COPY_NODE_FIELD(params); + + return newnode; + } + static AlterEnumStmt * _copyAlterEnumStmt(AlterEnumStmt *from) { *************** *** 4273,4278 **** copyObject(void *from) --- 4284,4292 ---- case T_CreateEnumStmt: retval = _copyCreateEnumStmt(from); break; + case T_CreateRangeStmt: + retval = _copyCreateRangeStmt(from); + break; case T_AlterEnumStmt: retval = _copyAlterEnumStmt(from); break; *** a/src/backend/nodes/equalfuncs.c --- b/src/backend/nodes/equalfuncs.c *************** *** 1439,1444 **** _equalCreateEnumStmt(CreateEnumStmt *a, CreateEnumStmt *b) --- 1439,1453 ---- } static bool + _equalCreateRangeStmt(CreateRangeStmt *a, CreateRangeStmt *b) + { + COMPARE_NODE_FIELD(typeName); + COMPARE_NODE_FIELD(params); + + return true; + } + + static bool _equalAlterEnumStmt(AlterEnumStmt *a, AlterEnumStmt *b) { COMPARE_NODE_FIELD(typeName); *************** *** 2826,2831 **** equal(void *a, void *b) --- 2835,2843 ---- case T_CreateEnumStmt: retval = _equalCreateEnumStmt(a, b); break; + case T_CreateRangeStmt: + retval = _equalCreateRangeStmt(a, b); + break; case T_AlterEnumStmt: retval = _equalAlterEnumStmt(a, b); break; *** a/src/backend/parser/gram.y --- b/src/backend/parser/gram.y *************** *** 4327,4332 **** DefineStmt: --- 4327,4339 ---- n->vals = $7; $$ = (Node *)n; } + | CREATE TYPE_P any_name AS RANGE definition + { + CreateRangeStmt *n = makeNode(CreateRangeStmt); + n->typeName = $3; + n->params = $6; + $$ = (Node *)n; + } | CREATE TEXT_P SEARCH PARSER any_name definition { DefineStmt *n = makeNode(DefineStmt); *** a/src/backend/parser/parse_coerce.c --- b/src/backend/parser/parse_coerce.c *************** *** 160,166 **** coerce_type(ParseState *pstate, Node *node, return node; } if (targetTypeId == ANYARRAYOID || ! targetTypeId == ANYENUMOID) { /* * Assume can_coerce_type verified that implicit coercion is okay. --- 160,167 ---- return node; } if (targetTypeId == ANYARRAYOID || ! targetTypeId == ANYENUMOID || ! targetTypeId == ANYRANGEOID) { /* * Assume can_coerce_type verified that implicit coercion is okay. *************** *** 1313,1318 **** check_generic_type_consistency(Oid *actual_arg_types, --- 1314,1321 ---- Oid elem_typeid = InvalidOid; Oid array_typeid = InvalidOid; Oid array_typelem; + Oid range_typeid = InvalidOid; + Oid range_typelem; bool have_anyelement = false; bool have_anynonarray = false; bool have_anyenum = false; *************** *** 1350,1355 **** check_generic_type_consistency(Oid *actual_arg_types, --- 1353,1367 ---- return false; array_typeid = actual_type; } + else if (decl_type == ANYRANGEOID) + { + if (actual_type == UNKNOWNOID) + continue; + actual_type = getBaseType(actual_type); + if (OidIsValid(range_typeid) && actual_type != range_typeid) + return false; + range_typeid = actual_type; + } } /* Get the element type based on the array type, if we have one */ *************** *** 1395,1400 **** check_generic_type_consistency(Oid *actual_arg_types, --- 1407,1433 ---- return false; } + /* Get the element type based on the range type, if we have one */ + if (OidIsValid(range_typeid)) + { + range_typelem = get_range_subtype(range_typeid); + if (!OidIsValid(range_typelem)) + return false; /* should be a range, but isn't */ + + if (!OidIsValid(elem_typeid)) + { + /* + * if we don't have an element type yet, use the one we just got + */ + elem_typeid = range_typelem; + } + else if (range_typelem != elem_typeid) + { + /* otherwise, they better match */ + return false; + } + } + /* Looks valid */ return true; } *************** *** 1475,1481 **** enforce_generic_type_consistency(Oid *actual_arg_types, --- 1508,1516 ---- bool have_unknowns = false; Oid elem_typeid = InvalidOid; Oid array_typeid = InvalidOid; + Oid range_typeid = InvalidOid; Oid array_typelem; + Oid range_typelem; bool have_anyelement = (rettype == ANYELEMENTOID || rettype == ANYNONARRAYOID || rettype == ANYENUMOID); *************** *** 1536,1541 **** enforce_generic_type_consistency(Oid *actual_arg_types, --- 1571,1596 ---- format_type_be(actual_type)))); array_typeid = actual_type; } + else if (decl_type == ANYRANGEOID) + { + have_generics = true; + if (actual_type == UNKNOWNOID) + { + have_unknowns = true; + continue; + } + if (allow_poly && decl_type == actual_type) + continue; /* no new information here */ + actual_type = getBaseType(actual_type); /* flatten domains */ + if (OidIsValid(range_typeid) && actual_type != range_typeid) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("arguments declared \"anyrange\" are not all alike"), + errdetail("%s versus %s", + format_type_be(range_typeid), + format_type_be(actual_type)))); + range_typeid = actual_type; + } } /* *************** *** 1581,1586 **** enforce_generic_type_consistency(Oid *actual_arg_types, --- 1636,1669 ---- format_type_be(elem_typeid)))); } } + /* Get the element type based on the range type, if we have one */ + else if (OidIsValid(range_typeid)) + { + range_typelem = get_range_subtype(range_typeid); + if (!OidIsValid(range_typelem)) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("argument declared \"anyrange\" is not a range but type %s", + format_type_be(range_typeid)))); + + if (!OidIsValid(elem_typeid)) + { + /* + * if we don't have an element type yet, use the one we just got + */ + elem_typeid = range_typelem; + } + else if (range_typelem != elem_typeid) + { + /* otherwise, they better match */ + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("argument declared \"anyrange\" is not consistent with argument declared \"anyelement\""), + errdetail("%s versus %s", + format_type_be(range_typeid), + format_type_be(elem_typeid)))); + } + } else if (!OidIsValid(elem_typeid)) { if (allow_poly) *************** *** 1647,1652 **** enforce_generic_type_consistency(Oid *actual_arg_types, --- 1730,1748 ---- } declared_arg_types[j] = array_typeid; } + else if (decl_type == ANYRANGEOID) + { + if (!OidIsValid(range_typeid)) + { + range_typeid = get_range_from_subtype(elem_typeid); + if (!OidIsValid(range_typeid)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("could not find range type for data type %s", + format_type_be(elem_typeid)))); + } + declared_arg_types[j] = range_typeid; + } } } *************** *** 1665,1670 **** enforce_generic_type_consistency(Oid *actual_arg_types, --- 1761,1781 ---- return array_typeid; } + /* if we return ANYRANGE use the appropriate argument type */ + if (rettype == ANYRANGEOID) + { + if (!OidIsValid(range_typeid)) + { + range_typeid = get_range_from_subtype(elem_typeid); + if (!OidIsValid(range_typeid)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("could not find range type for data type %s", + format_type_be(elem_typeid)))); + } + return range_typeid; + } + /* if we return ANYELEMENT use the appropriate argument type */ if (rettype == ANYELEMENTOID || rettype == ANYNONARRAYOID || *************** *** 1713,1719 **** resolve_generic_type(Oid declared_type, } else if (context_declared_type == ANYELEMENTOID || context_declared_type == ANYNONARRAYOID || ! context_declared_type == ANYENUMOID) { /* Use the array type corresponding to actual type */ Oid array_typeid = get_array_type(context_actual_type); --- 1824,1831 ---- } else if (context_declared_type == ANYELEMENTOID || context_declared_type == ANYNONARRAYOID || ! context_declared_type == ANYENUMOID || ! context_declared_type == ANYRANGEOID) { /* Use the array type corresponding to actual type */ Oid array_typeid = get_array_type(context_actual_type); *************** *** 1728,1734 **** resolve_generic_type(Oid declared_type, } else if (declared_type == ANYELEMENTOID || declared_type == ANYNONARRAYOID || ! declared_type == ANYENUMOID) { if (context_declared_type == ANYARRAYOID) { --- 1840,1847 ---- } else if (declared_type == ANYELEMENTOID || declared_type == ANYNONARRAYOID || ! declared_type == ANYENUMOID || ! declared_type == ANYRANGEOID) { if (context_declared_type == ANYARRAYOID) { *************** *** 1743,1748 **** resolve_generic_type(Oid declared_type, --- 1856,1873 ---- format_type_be(context_base_type)))); return array_typelem; } + else if (context_declared_type == ANYRANGEOID) + { + /* Use the element type corresponding to actual type */ + Oid range_typelem = get_range_subtype(context_actual_type); + + if (!OidIsValid(range_typelem)) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("argument declared \"anyrange\" is not an range but type %s", + format_type_be(context_actual_type)))); + return range_typelem; + } else if (context_declared_type == ANYELEMENTOID || context_declared_type == ANYNONARRAYOID || context_declared_type == ANYENUMOID) *************** *** 1856,1861 **** IsBinaryCoercible(Oid srctype, Oid targettype) --- 1981,1991 ---- if (type_is_enum(srctype)) return true; + /* Also accept any enum type as coercible to ANYRANGE */ + if (targettype == ANYRANGEOID) + if (type_is_range(srctype)) + return true; + /* Also accept any composite type as coercible to RECORD */ if (targettype == RECORDOID) if (ISCOMPLEX(srctype)) *** a/src/backend/tcop/utility.c --- b/src/backend/tcop/utility.c *************** *** 202,207 **** check_xact_readonly(Node *parsetree) --- 202,208 ---- case T_CreateTrigStmt: case T_CompositeTypeStmt: case T_CreateEnumStmt: + case T_CreateRangeStmt: case T_AlterEnumStmt: case T_ViewStmt: case T_DropCastStmt: *************** *** 913,918 **** standard_ProcessUtility(Node *parsetree, --- 914,923 ---- DefineEnum((CreateEnumStmt *) parsetree); break; + case T_CreateRangeStmt: + DefineRange((CreateRangeStmt *) parsetree); + break; + case T_AlterEnumStmt: /* ALTER TYPE (enum) */ /* *************** *** 1897,1902 **** CreateCommandTag(Node *parsetree) --- 1902,1911 ---- tag = "CREATE TYPE"; break; + case T_CreateRangeStmt: + tag = "CREATE TYPE"; + break; + case T_AlterEnumStmt: tag = "ALTER TYPE"; break; *************** *** 2444,2449 **** GetCommandLogLevel(Node *parsetree) --- 2453,2462 ---- lev = LOGSTMT_DDL; break; + case T_CreateRangeStmt: + lev = LOGSTMT_DDL; + break; + case T_AlterEnumStmt: lev = LOGSTMT_DDL; break; *** a/src/backend/utils/adt/Makefile --- b/src/backend/utils/adt/Makefile *************** *** 20,27 **** OBJS = acl.o arrayfuncs.o array_userfuncs.o arrayutils.o bool.o \ enum.o float.o format_type.o \ geo_ops.o geo_selfuncs.o int.o int8.o like.o lockfuncs.o \ misc.o nabstime.o name.o numeric.o numutils.o \ ! oid.o oracle_compat.o pseudotypes.o rowtypes.o \ ! regexp.o regproc.o ruleutils.o selfuncs.o \ tid.o timestamp.o varbit.o varchar.o varlena.o version.o xid.o \ network.o mac.o inet_cidr_ntop.o inet_net_pton.o \ ri_triggers.o pg_lzcompress.o pg_locale.o formatting.o \ --- 20,27 ---- enum.o float.o format_type.o \ geo_ops.o geo_selfuncs.o int.o int8.o like.o lockfuncs.o \ misc.o nabstime.o name.o numeric.o numutils.o \ ! oid.o oracle_compat.o pseudotypes.o rangetypes.o rangetypes_gist.o \ ! rowtypes.o regexp.o regproc.o ruleutils.o selfuncs.o \ tid.o timestamp.o varbit.o varchar.o varlena.o version.o xid.o \ network.o mac.o inet_cidr_ntop.o inet_net_pton.o \ ri_triggers.o pg_lzcompress.o pg_locale.o formatting.o \ *** a/src/backend/utils/adt/date.c --- b/src/backend/utils/adt/date.c *************** *** 888,893 **** date_timestamp(PG_FUNCTION_ARGS) --- 888,900 ---- PG_RETURN_TIMESTAMP(result); } + Datum + date_float8(PG_FUNCTION_ARGS) + { + DateADT dateVal = PG_GETARG_DATEADT(0); + + PG_RETURN_FLOAT8((float8) dateVal); + } /* timestamp_date() * Convert timestamp to date data type. *** a/src/backend/utils/adt/pseudotypes.c --- b/src/backend/utils/adt/pseudotypes.c *************** *** 25,30 **** --- 25,31 ---- #include "libpq/pqformat.h" #include "utils/array.h" #include "utils/builtins.h" + #include "utils/rangetypes.h" /* *************** *** 187,192 **** anyenum_out(PG_FUNCTION_ARGS) --- 188,216 ---- return enum_out(fcinfo); } + /* + * anyrange_in - input routine for pseudo-type ANYRANGE. + */ + Datum + anyrange_in(PG_FUNCTION_ARGS) + { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot accept a value of type anyrange"))); + + PG_RETURN_VOID(); /* keep compiler quiet */ + } + + /* + * anyrange_out - output routine for pseudo-type ANYRANGE. + * + * We may as well allow this, since range_out will in fact work. + */ + Datum + anyrange_out(PG_FUNCTION_ARGS) + { + return range_out(fcinfo); + } /* * void_in - input routine for pseudo-type VOID. *** /dev/null --- b/src/backend/utils/adt/rangetypes.c *************** *** 0 **** --- 1,1864 ---- + /*------------------------------------------------------------------------- + * + * rangetypes.c + * I/O functions, operators, and support functions for range types + * + * Copyright (c) 2006-2011, PostgreSQL Global Development Group + * + * + * IDENTIFICATION + * src/backend/utils/adt/rangetypes.c + * + *------------------------------------------------------------------------- + */ + #include "postgres.h" + + #include "access/hash.h" + #include "access/nbtree.h" + #include "catalog/pg_opclass.h" + #include "catalog/pg_range.h" + #include "catalog/pg_type.h" + #include "fmgr.h" + #include "lib/stringinfo.h" + #include "utils/builtins.h" + #include "utils/date.h" + #include "utils/fmgroids.h" + #include "utils/lsyscache.h" + #include "utils/rangetypes.h" + #include "utils/syscache.h" + #include "utils/typcache.h" + + #define TYPE_IS_PACKABLE(typlen, typstorage) \ + (typlen == -1 && typstorage != 'p') + + /* flags */ + #define RANGE_EMPTY 0x01 + #define RANGE_LB_INC 0x02 + #define RANGE_LB_NULL 0x04 + #define RANGE_LB_INF 0x08 + #define RANGE_UB_INC 0x10 + #define RANGE_UB_NULL 0x20 + #define RANGE_UB_INF 0x40 + + #define RANGE_HAS_LBOUND(flags) (!(flags & (RANGE_EMPTY | \ + RANGE_LB_NULL | \ + RANGE_LB_INF))) + + #define RANGE_HAS_UBOUND(flags) (!(flags & (RANGE_EMPTY | \ + RANGE_UB_NULL | \ + RANGE_UB_INF))) + typedef enum + { + RANGE_PSTATE_INIT, + RANGE_PSTATE_PRE_LB, + RANGE_PSTATE_LB, + RANGE_PSTATE_SEP, + RANGE_PSTATE_PRE_UB, + RANGE_PSTATE_UB, + RANGE_PSTATE_UB_INC, + RANGE_PSTATE_DONE + } RangePState; + + + static void range_parse(const char *input_str, char *flags, + char **lbound_str, char **ubound_str); + static char *range_deparse(char flags, char *lbound_str, char *ubound_str); + static Datum range_make2(PG_FUNCTION_ARGS); + static bool range_contains_internal(RangeType *r1, RangeType *r2); + static Size datum_compute_size(Size sz, Datum datum, bool typbyval, + char typalign, int16 typlen, char typstorage); + static Pointer datum_write(Pointer ptr, Datum datum, bool typbyval, + char typalign, int16 typlen, char typstorage); + static void range_subtype_cmpfn(Oid rngtypid, FmgrInfo *flinfo, + Oid *collation); + + /* + *---------------------------------------------------------- + * I/O FUNCTIONS + *---------------------------------------------------------- + */ + + Datum + range_in(PG_FUNCTION_ARGS) + { + char *input_str = PG_GETARG_CSTRING(0); + Oid rngtypoid = PG_GETARG_OID(1); + Oid typmod = PG_GETARG_INT32(2); + + char flags; + Datum range; + char *lbound_str; + char *ubound_str; + + Oid subtype; + regproc subInput; + FmgrInfo subInputFn; + Oid ioParam; + + RangeBound lower; + RangeBound upper; + + if (rngtypoid == ANYRANGEOID) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot accept a value of type anyrange"))); + + subtype = get_range_subtype(rngtypoid); + + /* parse */ + range_parse(input_str, &flags, &lbound_str, &ubound_str); + + /* input */ + getTypeInputInfo(subtype, &subInput, &ioParam); + fmgr_info(subInput, &subInputFn); + + lower.rngtypid = rngtypoid; + lower.infinite = flags & RANGE_LB_INF; + lower.inclusive = flags & RANGE_LB_INC; + lower.lower = true; + upper.rngtypid = rngtypoid; + upper.infinite = flags & RANGE_UB_INF; + upper.inclusive = flags & RANGE_UB_INC; + upper.lower = false; + + if (RANGE_HAS_LBOUND(flags)) + lower.val = InputFunctionCall(&subInputFn, lbound_str, + ioParam, typmod); + if (RANGE_HAS_UBOUND(flags)) + upper.val = InputFunctionCall(&subInputFn, ubound_str, + ioParam, typmod); + + /* serialize and canonicalize */ + range = make_range(&lower, &upper, flags & RANGE_EMPTY); + + PG_RETURN_RANGE(range); + } + + Datum + range_out(PG_FUNCTION_ARGS) + { + RangeType *range = PG_GETARG_RANGE(0); + + Oid subtype; + + regproc subOutput; + FmgrInfo subOutputFn; + bool isVarlena; + + char flags = 0; + char *lbound_str = NULL; + char *ubound_str = NULL; + char *output_str; + + bool empty; + RangeBound lower; + RangeBound upper; + + /* deserialize */ + range_deserialize(range, &lower, &upper, &empty); + + if (lower.rngtypid != upper.rngtypid) + elog(ERROR, "range types do not match"); + + subtype = get_range_subtype(lower.rngtypid); + + if (empty) + flags |= RANGE_EMPTY; + + flags |= (lower.inclusive) ? RANGE_LB_INC : 0; + flags |= (lower.infinite) ? RANGE_LB_INF : 0; + flags |= (upper.inclusive) ? RANGE_UB_INC : 0; + flags |= (upper.infinite) ? RANGE_UB_INF : 0; + + /* output */ + getTypeOutputInfo(subtype, &subOutput, &isVarlena); + fmgr_info(subOutput, &subOutputFn); + + if (RANGE_HAS_LBOUND(flags)) + lbound_str = OutputFunctionCall(&subOutputFn, lower.val); + if (RANGE_HAS_UBOUND(flags)) + ubound_str = OutputFunctionCall(&subOutputFn, upper.val); + + /* deparse */ + output_str = range_deparse(flags, lbound_str, ubound_str); + + PG_RETURN_CSTRING(output_str); + } + + Datum + range_recv(PG_FUNCTION_ARGS) + { + PG_RETURN_VOID(); + } + + Datum + range_send(PG_FUNCTION_ARGS) + { + PG_RETURN_VOID(); + } + + + /* + *---------------------------------------------------------- + * GENERIC FUNCTIONS + *---------------------------------------------------------- + */ + + + /* constructors */ + + Datum + range_make1(PG_FUNCTION_ARGS) + { + Datum arg = PG_GETARG_DATUM(0); + Oid subtype = get_fn_expr_argtype(fcinfo->flinfo,0); + Oid rngtypid = get_range_from_subtype(subtype); + RangeType *range; + RangeBound lower; + RangeBound upper; + + if (PG_ARGISNULL(0)) + elog(ERROR, "NULL range boundaries are not supported"); + + if (!OidIsValid(subtype) || !OidIsValid(rngtypid)) + elog(ERROR, "cannot determine argument types"); + + lower.rngtypid = rngtypid; + lower.inclusive = true; + lower.val = arg; + lower.lower = true; + lower.infinite = false; + upper.rngtypid = rngtypid; + upper.inclusive = true; + upper.val = arg; + upper.lower = false; + upper.infinite = false; + + range = DatumGetRangeType(make_range(&lower, &upper, false)); + + PG_RETURN_RANGE(range); + } + + Datum + range_linf_(PG_FUNCTION_ARGS) + { + Datum arg = PG_GETARG_DATUM(0); + Oid subtype = get_fn_expr_argtype(fcinfo->flinfo,0); + Oid rngtypid = get_range_from_subtype(subtype); + RangeType *range; + RangeBound lower; + RangeBound upper; + + if (PG_ARGISNULL(0)) + elog(ERROR, "NULL range boundaries are not supported"); + + lower.rngtypid = rngtypid; + lower.inclusive = false; + lower.infinite = true; + lower.lower = true; + lower.val = (Datum) 0; + + upper.rngtypid = rngtypid; + upper.inclusive = false; + upper.infinite = false; + upper.lower = false; + upper.val = arg; + + range = DatumGetRangeType(make_range(&lower, &upper, false)); + + PG_RETURN_RANGE(range); + } + + Datum + range_uinf_(PG_FUNCTION_ARGS) + { + Datum arg = PG_GETARG_DATUM(0); + Oid subtype = get_fn_expr_argtype(fcinfo->flinfo,0); + Oid rngtypid = get_range_from_subtype(subtype); + RangeType *range; + RangeBound lower; + RangeBound upper; + + if (PG_ARGISNULL(0)) + elog(ERROR, "NULL range boundaries are not supported"); + + lower.rngtypid = rngtypid; + lower.inclusive = false; + lower.infinite = false; + lower.lower = true; + lower.val = arg; + + upper.rngtypid = rngtypid; + upper.inclusive = false; + upper.infinite = true; + upper.lower = false; + upper.val = (Datum) 0; + + range = DatumGetRangeType(make_range(&lower, &upper, false)); + + PG_RETURN_RANGE(range); + } + + Datum + range_linfi(PG_FUNCTION_ARGS) + { + Datum arg = PG_GETARG_DATUM(0); + Oid subtype = get_fn_expr_argtype(fcinfo->flinfo,0); + Oid rngtypid = get_range_from_subtype(subtype); + RangeType *range; + RangeBound lower; + RangeBound upper; + + if (PG_ARGISNULL(0)) + elog(ERROR, "NULL range boundaries are not supported"); + + lower.rngtypid = rngtypid; + lower.inclusive = false; + lower.infinite = true; + lower.lower = true; + lower.val = (Datum) 0; + + upper.rngtypid = rngtypid; + upper.inclusive = true; + upper.infinite = false; + upper.lower = false; + upper.val = arg; + + range = DatumGetRangeType(make_range(&lower, &upper, false)); + + PG_RETURN_RANGE(range); + } + + Datum + range_uinfi(PG_FUNCTION_ARGS) + { + Datum arg = PG_GETARG_DATUM(0); + Oid subtype = get_fn_expr_argtype(fcinfo->flinfo,0); + Oid rngtypid = get_range_from_subtype(subtype); + RangeType *range; + RangeBound lower; + RangeBound upper; + + if (PG_ARGISNULL(0)) + elog(ERROR, "NULL range boundaries are not supported"); + + lower.rngtypid = rngtypid; + lower.inclusive = true; + lower.infinite = false; + lower.lower = true; + lower.val = arg; + + upper.rngtypid = rngtypid; + upper.inclusive = false; + upper.infinite = true; + upper.lower = false; + upper.val = (Datum) 0; + + range = DatumGetRangeType(make_range(&lower, &upper, false)); + + PG_RETURN_RANGE(range); + } + + Datum + range(PG_FUNCTION_ARGS) + { + return range_make2(fcinfo); + } + + Datum + range__(PG_FUNCTION_ARGS) + { + return range_make2(fcinfo); + } + + Datum + range_i(PG_FUNCTION_ARGS) + { + return range_make2(fcinfo); + } + + Datum + rangei_(PG_FUNCTION_ARGS) + { + return range_make2(fcinfo); + } + + Datum + rangeii(PG_FUNCTION_ARGS) + { + return range_make2(fcinfo); + } + + /* range -> subtype */ + Datum + range_lower(PG_FUNCTION_ARGS) + { + RangeType *r1 = PG_GETARG_RANGE(0); + RangeBound lower; + RangeBound upper; + bool empty; + + range_deserialize(r1, &lower, &upper, &empty); + + if (empty) + elog(ERROR, "range is empty"); + if (lower.infinite) + elog(ERROR, "range lower bound is infinite"); + + PG_RETURN_DATUM(lower.val); + } + + Datum + range_upper(PG_FUNCTION_ARGS) + { + RangeType *r1 = PG_GETARG_RANGE(0); + RangeBound lower; + RangeBound upper; + bool empty; + + range_deserialize(r1, &lower, &upper, &empty); + + if (empty) + elog(ERROR, "range is empty"); + if (upper.infinite) + elog(ERROR, "range lower bound is infinite"); + + PG_RETURN_DATUM(upper.val); + } + + + /* range -> bool */ + Datum + range_empty(PG_FUNCTION_ARGS) + { + RangeType *r1 = PG_GETARG_RANGE(0); + RangeBound lower; + RangeBound upper; + bool empty; + + range_deserialize(r1, &lower, &upper, &empty); + + PG_RETURN_BOOL(empty); + } + + Datum + range_non_empty(PG_FUNCTION_ARGS) + { + RangeType *r1 = PG_GETARG_RANGE(0); + RangeBound lower; + RangeBound upper; + bool empty; + + range_deserialize(r1, &lower, &upper, &empty); + + PG_RETURN_BOOL(!empty); + } + + Datum + range_lower_inc(PG_FUNCTION_ARGS) + { + RangeType *r1 = PG_GETARG_RANGE(0); + RangeBound lower; + RangeBound upper; + bool empty; + + range_deserialize(r1, &lower, &upper, &empty); + + PG_RETURN_BOOL(lower.inclusive); + } + + Datum + range_upper_inc(PG_FUNCTION_ARGS) + { + RangeType *r1 = PG_GETARG_RANGE(0); + RangeBound lower; + RangeBound upper; + bool empty; + + range_deserialize(r1, &lower, &upper, &empty); + + PG_RETURN_BOOL(upper.inclusive); + } + + Datum + range_lower_inf(PG_FUNCTION_ARGS) + { + RangeType *r1 = PG_GETARG_RANGE(0); + RangeBound lower; + RangeBound upper; + bool empty; + + range_deserialize(r1, &lower, &upper, &empty); + + PG_RETURN_BOOL(lower.infinite); + } + + Datum + range_upper_inf(PG_FUNCTION_ARGS) + { + RangeType *r1 = PG_GETARG_RANGE(0); + RangeBound lower; + RangeBound upper; + bool empty; + + range_deserialize(r1, &lower, &upper, &empty); + + PG_RETURN_BOOL(upper.infinite); + } + + + /* range, range -> bool */ + Datum + range_eq(PG_FUNCTION_ARGS) + { + RangeType *r1 = PG_GETARG_RANGE(0); + RangeType *r2 = PG_GETARG_RANGE(1); + + RangeBound lower1, lower2; + RangeBound upper1, upper2; + bool empty1, empty2; + + range_deserialize(r1, &lower1, &upper1, &empty1); + range_deserialize(r2, &lower2, &upper2, &empty2); + + if (lower1.rngtypid != upper1.rngtypid || + lower1.rngtypid != lower2.rngtypid || + lower1.rngtypid != upper2.rngtypid) + elog(ERROR, "range types do not match"); + + if (empty1 && empty2) + PG_RETURN_BOOL(true); + if (empty1 != empty2) + PG_RETURN_BOOL(false); + + if (range_cmp_bounds(&lower1, &lower2) != 0) + PG_RETURN_BOOL(false); + + if (range_cmp_bounds(&upper1, &upper2) != 0) + PG_RETURN_BOOL(false); + + PG_RETURN_BOOL(true); + } + + Datum + range_ne(PG_FUNCTION_ARGS) + { + bool eq = DatumGetBool(range_eq(fcinfo)); + + PG_RETURN_BOOL(!eq); + } + + Datum + range_contains_elem(PG_FUNCTION_ARGS) + { + RangeType *r1 = PG_GETARG_RANGE(0); + RangeType *r2; + Datum val = PG_GETARG_DATUM(1); + + RangeBound lower1, lower2; + RangeBound upper1, upper2; + bool empty1; + + range_deserialize(r1, &lower1, &upper1, &empty1); + + lower2.rngtypid = lower1.rngtypid; + lower2.inclusive = true; + lower2.infinite = false; + lower2.lower = true; + lower2.val = val; + + upper2.rngtypid = lower1.rngtypid; + upper2.inclusive = true; + upper2.infinite = false; + upper2.lower = false; + upper2.val = val; + + r2 = DatumGetRangeType(make_range(&lower2, &upper2, false)); + + PG_RETURN_BOOL(range_contains_internal(r1, r2)); + } + + Datum + range_contains(PG_FUNCTION_ARGS) + { + RangeType *r1 = PG_GETARG_RANGE(0); + RangeType *r2 = PG_GETARG_RANGE(1); + + PG_RETURN_BOOL(range_contains_internal(r1, r2)); + } + + Datum + elem_contained_by_range(PG_FUNCTION_ARGS) + { + RangeType *r1 = PG_GETARG_RANGE(1); + RangeType *r2; + Datum val = PG_GETARG_DATUM(0); + + RangeBound lower1, lower2; + RangeBound upper1, upper2; + bool empty1; + + range_deserialize(r1, &lower1, &upper1, &empty1); + + lower2.rngtypid = lower1.rngtypid; + lower2.inclusive = true; + lower2.infinite = false; + lower2.lower = true; + lower2.val = val; + + upper2.rngtypid = lower1.rngtypid; + upper2.inclusive = true; + upper2.infinite = false; + upper2.lower = false; + upper2.val = val; + + r2 = DatumGetRangeType(make_range(&lower2, &upper2, false)); + + PG_RETURN_BOOL(range_contains_internal(r1, r2)); + } + + Datum + range_contained_by(PG_FUNCTION_ARGS) + { + RangeType *r1 = PG_GETARG_RANGE(0); + RangeType *r2 = PG_GETARG_RANGE(1); + + PG_RETURN_BOOL(range_contains_internal(r2, r1)); + } + + Datum + range_before(PG_FUNCTION_ARGS) + { + RangeType *r1 = PG_GETARG_RANGE(0); + RangeType *r2 = PG_GETARG_RANGE(1); + + RangeBound lower1, lower2; + RangeBound upper1, upper2; + bool empty1, empty2; + + range_deserialize(r1, &lower1, &upper1, &empty1); + range_deserialize(r2, &lower2, &upper2, &empty2); + + if (lower1.rngtypid != upper1.rngtypid || + lower1.rngtypid != lower2.rngtypid || + lower1.rngtypid != upper2.rngtypid) + elog(ERROR, "range types do not match"); + + if (empty1 || empty2) + elog(ERROR, "empty range"); + + if (range_cmp_bounds(&upper1, &lower2) < 0) + PG_RETURN_BOOL(true); + else + PG_RETURN_BOOL(false); + } + + Datum + range_after(PG_FUNCTION_ARGS) + { + RangeType *r1 = PG_GETARG_RANGE(0); + RangeType *r2 = PG_GETARG_RANGE(1); + + RangeBound lower1, lower2; + RangeBound upper1, upper2; + bool empty1, empty2; + + range_deserialize(r1, &lower1, &upper1, &empty1); + range_deserialize(r2, &lower2, &upper2, &empty2); + + if (lower1.rngtypid != upper1.rngtypid || + lower1.rngtypid != lower2.rngtypid || + lower1.rngtypid != upper2.rngtypid) + elog(ERROR, "range types do not match"); + + if (empty1 || empty2) + elog(ERROR, "empty range"); + + if (range_cmp_bounds(&lower1, &upper2) > 0) + PG_RETURN_BOOL(true); + else + PG_RETURN_BOOL(false); + } + + Datum range_adjacent(PG_FUNCTION_ARGS) + { + RangeType *r1 = PG_GETARG_RANGE(0); + RangeType *r2 = PG_GETARG_RANGE(1); + + RangeBound lower1, lower2; + RangeBound upper1, upper2; + bool empty1, empty2; + + FmgrInfo flinfo; + Oid collation; + + range_deserialize(r1, &lower1, &upper1, &empty1); + range_deserialize(r2, &lower2, &upper2, &empty2); + + if (lower1.rngtypid != upper1.rngtypid || + lower1.rngtypid != lower2.rngtypid || + lower1.rngtypid != upper2.rngtypid) + elog(ERROR, "range types do not match"); + + if (empty1 || empty2) + elog(ERROR, "empty range"); + + /* + * For two ranges to be adjacent, the lower boundary of one range + * has to match the upper boundary of the other. However, the + * inclusivity of those two boundaries must also be different. + * + * The semantics for range_cmp_bounds aren't quite what we need + * here, so we do the comparison more directly. + */ + + range_subtype_cmpfn(lower1.rngtypid, &flinfo, &collation); + + if (lower1.inclusive != upper2.inclusive) + { + if (DatumGetInt32(FunctionCall2Coll(&flinfo, collation, + lower1.val, upper2.val)) == 0) + PG_RETURN_BOOL(true); + } + + if (upper1.inclusive != lower2.inclusive) + { + if (DatumGetInt32(FunctionCall2Coll(&flinfo, collation, + upper1.val, lower2.val)) == 0) + PG_RETURN_BOOL(true); + } + + PG_RETURN_BOOL(false); + } + + Datum + range_overlaps(PG_FUNCTION_ARGS) + { + RangeType *r1 = PG_GETARG_RANGE(0); + RangeType *r2 = PG_GETARG_RANGE(1); + + RangeBound lower1, lower2; + RangeBound upper1, upper2; + bool empty1, empty2; + + range_deserialize(r1, &lower1, &upper1, &empty1); + range_deserialize(r2, &lower2, &upper2, &empty2); + + if (lower1.rngtypid != upper1.rngtypid || + lower1.rngtypid != lower2.rngtypid || + lower1.rngtypid != upper2.rngtypid) + elog(ERROR, "range types do not match"); + + if (empty1 || empty2) + PG_RETURN_BOOL(false); + + if (range_cmp_bounds(&lower1, &lower2) >= 0 && + range_cmp_bounds(&lower1, &upper2) <= 0) + PG_RETURN_BOOL(true); + + if (range_cmp_bounds(&lower2, &lower1) >= 0 && + range_cmp_bounds(&lower2, &upper1) <= 0) + PG_RETURN_BOOL(true); + + PG_RETURN_BOOL(false); + } + + Datum + range_overleft(PG_FUNCTION_ARGS) + { + RangeType *r1 = PG_GETARG_RANGE(0); + RangeType *r2 = PG_GETARG_RANGE(1); + + RangeBound lower1, lower2; + RangeBound upper1, upper2; + bool empty1, empty2; + + range_deserialize(r1, &lower1, &upper1, &empty1); + range_deserialize(r2, &lower2, &upper2, &empty2); + + if (lower1.rngtypid != upper1.rngtypid || + lower1.rngtypid != lower2.rngtypid || + lower1.rngtypid != upper2.rngtypid) + elog(ERROR, "range types do not match"); + + if (empty1 || empty2) + PG_RETURN_BOOL(false); + + if (range_cmp_bounds(&upper1, &upper2) <= 0) + PG_RETURN_BOOL(true); + + PG_RETURN_BOOL(false); + } + + Datum + range_overright(PG_FUNCTION_ARGS) + { + RangeType *r1 = PG_GETARG_RANGE(0); + RangeType *r2 = PG_GETARG_RANGE(1); + + RangeBound lower1, lower2; + RangeBound upper1, upper2; + bool empty1, empty2; + + range_deserialize(r1, &lower1, &upper1, &empty1); + range_deserialize(r2, &lower2, &upper2, &empty2); + + if (lower1.rngtypid != upper1.rngtypid || + lower1.rngtypid != lower2.rngtypid || + lower1.rngtypid != upper2.rngtypid) + elog(ERROR, "range types do not match"); + + if (empty1 || empty2) + PG_RETURN_BOOL(false); + + if (range_cmp_bounds(&lower1, &lower2) >= 0) + PG_RETURN_BOOL(true); + + PG_RETURN_BOOL(false); + } + + + /* range, range -> range */ + Datum + range_minus(PG_FUNCTION_ARGS) + { + RangeType *r1 = PG_GETARG_RANGE(0); + RangeType *r2 = PG_GETARG_RANGE(1); + + RangeBound lower1, lower2; + RangeBound upper1, upper2; + bool empty1, empty2; + + int cmp_l1l2, cmp_l1u2, cmp_u1l2, cmp_u1u2; + + range_deserialize(r1, &lower1, &upper1, &empty1); + range_deserialize(r2, &lower2, &upper2, &empty2); + + if (lower1.rngtypid != upper1.rngtypid || + lower1.rngtypid != lower2.rngtypid || + lower1.rngtypid != upper2.rngtypid) + elog(ERROR, "range types do not match"); + + if (empty1 || empty2) + PG_RETURN_RANGE(r1); + + cmp_l1l2 = range_cmp_bounds(&lower1, &lower2); + cmp_l1u2 = range_cmp_bounds(&lower1, &upper2); + cmp_u1l2 = range_cmp_bounds(&upper1, &lower2); + cmp_u1u2 = range_cmp_bounds(&upper1, &upper2); + + if (cmp_l1l2 < 0 && cmp_u1u2 > 0) + elog(ERROR, "range_minus resulted in two ranges"); + + if (cmp_l1u2 > 0 || cmp_u1l2 < 0) + PG_RETURN_RANGE(r1); + + if (cmp_l1l2 >= 0 && cmp_u1u2 <= 0) + PG_RETURN_RANGE(make_empty_range(lower1.rngtypid)); + + if (cmp_l1l2 <= 0 && cmp_u1l2 >= 0 && cmp_u1u2 <= 0) + { + lower2.inclusive = !lower2.inclusive; + lower2.lower = false; /* it will become the upper bound */ + PG_RETURN_RANGE(make_range(&lower1, &lower2, false)); + } + + if (cmp_l1l2 >= 0 && cmp_u1u2 >= 0 && cmp_l1u2 <= 0) + { + upper2.inclusive = !upper2.inclusive; + upper2.lower = true; /* it will become the lower bound */ + PG_RETURN_RANGE(make_range(&upper2, &upper1, false)); + } + + elog(ERROR, "unexpected error in range_minus"); + PG_RETURN_VOID(); + } + + Datum + range_union(PG_FUNCTION_ARGS) + { + RangeType *r1 = PG_GETARG_RANGE(0); + RangeType *r2 = PG_GETARG_RANGE(1); + + RangeBound lower1, lower2; + RangeBound upper1, upper2; + bool empty1, empty2; + RangeBound *result_lower; + RangeBound *result_upper; + + range_deserialize(r1, &lower1, &upper1, &empty1); + range_deserialize(r2, &lower2, &upper2, &empty2); + + if (empty1) + PG_RETURN_RANGE(r2); + if (empty2) + PG_RETURN_RANGE(r1); + + if (!DatumGetBool(range_overlaps(fcinfo)) && + !DatumGetBool(range_adjacent(fcinfo))) + elog(ERROR, "range union resulted in two ranges"); + + if (range_cmp_bounds(&lower1, &lower2) < 0) + result_lower = &lower1; + else + result_lower = &lower2; + + if (range_cmp_bounds(&upper1, &upper2) > 0) + result_upper = &upper1; + else + result_upper = &upper2; + + PG_RETURN_RANGE(make_range(result_lower, result_upper, false)); + } + + Datum + range_intersect(PG_FUNCTION_ARGS) + { + RangeType *r1 = PG_GETARG_RANGE(0); + RangeType *r2 = PG_GETARG_RANGE(1); + + RangeBound lower1, lower2; + RangeBound upper1, upper2; + bool empty1, empty2; + RangeBound *result_lower; + RangeBound *result_upper; + + range_deserialize(r1, &lower1, &upper1, &empty1); + range_deserialize(r2, &lower2, &upper2, &empty2); + + if (empty1 || empty2 || !DatumGetBool(range_overlaps(fcinfo))) + PG_RETURN_RANGE(make_empty_range(lower1.rngtypid)); + + if (range_cmp_bounds(&lower1, &lower2) >= 0) + result_lower = &lower1; + else + result_lower = &lower2; + + if (range_cmp_bounds(&upper1, &upper2) <= 0) + result_upper = &upper1; + else + result_upper = &upper2; + + PG_RETURN_RANGE(make_range(result_lower, result_upper, false)); + } + + /* Btree support */ + + Datum + range_cmp(PG_FUNCTION_ARGS) + { + RangeType *r1 = PG_GETARG_RANGE(0); + RangeType *r2 = PG_GETARG_RANGE(1); + + RangeBound lower1, lower2; + RangeBound upper1, upper2; + bool empty1, empty2; + + int cmp; + + range_deserialize(r1, &lower1, &upper1, &empty1); + range_deserialize(r2, &lower2, &upper2, &empty2); + + if (lower1.rngtypid != upper1.rngtypid || + lower1.rngtypid != lower2.rngtypid || + lower1.rngtypid != upper2.rngtypid) + elog(ERROR, "range types do not match"); + + if (empty1 && empty2) + PG_RETURN_INT32(0); + else if (empty1) + PG_RETURN_INT32(-1); + else if (empty2) + PG_RETURN_INT32(1); + + if ((cmp = range_cmp_bounds(&lower1, &lower2)) != 0) + PG_RETURN_INT32(cmp); + + PG_RETURN_INT32(range_cmp_bounds(&upper1, &upper2)); + } + + Datum + range_lt(PG_FUNCTION_ARGS) + { + int cmp = range_cmp(fcinfo); + PG_RETURN_BOOL(cmp < 0); + } + + Datum + range_le(PG_FUNCTION_ARGS) + { + int cmp = range_cmp(fcinfo); + PG_RETURN_BOOL(cmp <= 0); + } + + Datum + range_ge(PG_FUNCTION_ARGS) + { + int cmp = range_cmp(fcinfo); + PG_RETURN_BOOL(cmp >= 0); + } + + Datum + range_gt(PG_FUNCTION_ARGS) + { + int cmp = range_cmp(fcinfo); + PG_RETURN_BOOL(cmp > 0); + } + + /* Hash support */ + Datum + hash_range(PG_FUNCTION_ARGS) + { + RangeType *r = PG_GETARG_RANGE(0); + RangeBound lower; + RangeBound upper; + bool empty; + char flags = 0; + uint32 lower_hash = 0; + uint32 upper_hash = 0; + uint32 result = 0; + + TypeCacheEntry *typentry; + Oid subtype; + FunctionCallInfoData locfcinfo; + + + range_deserialize(r, &lower, &upper, &empty); + + if (lower.rngtypid != upper.rngtypid) + elog(ERROR, "range types do not match"); + + if (empty) + flags |= RANGE_EMPTY; + + flags |= (lower.inclusive) ? RANGE_LB_INC : 0; + flags |= (lower.infinite) ? RANGE_LB_INF : 0; + flags |= (upper.inclusive) ? RANGE_UB_INC : 0; + flags |= (upper.infinite) ? RANGE_UB_INF : 0; + + subtype = get_range_subtype(lower.rngtypid); + + /* + * We arrange to look up the hash function only once per series of + * calls, assuming the subtype doesn't change underneath us. The + * typcache is used so that we have no memory leakage when being + * used as an index support function. + */ + typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra; + if (typentry == NULL || typentry->type_id != subtype) + { + typentry = lookup_type_cache(subtype, TYPECACHE_HASH_PROC_FINFO); + if (!OidIsValid(typentry->hash_proc_finfo.fn_oid)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_FUNCTION), + errmsg("could not identify a hash function for type %s", + format_type_be(subtype)))); + fcinfo->flinfo->fn_extra = (void *) typentry; + } + + /* + * Apply the hash function to each array element (the hash + * function shouldn't care about the collation). + */ + InitFunctionCallInfoData(locfcinfo, &typentry->hash_proc_finfo, 1, + InvalidOid, NULL, NULL); + + if (RANGE_HAS_LBOUND(flags)) + { + locfcinfo.arg[0] = lower.val; + locfcinfo.argnull[0] = false; + locfcinfo.isnull = false; + lower_hash = DatumGetUInt32(FunctionCallInvoke(&locfcinfo)); + } + if (RANGE_HAS_UBOUND(flags)) + { + locfcinfo.arg[0] = upper.val; + locfcinfo.argnull[0] = false; + locfcinfo.isnull = false; + upper_hash = DatumGetUInt32(FunctionCallInvoke(&locfcinfo)); + } + + result = hash_uint32((uint32) flags); + result = (result << 1) | (result >> 31); + result ^= lower_hash; + result = (result << 1) | (result >> 31); + result ^= upper_hash; + + PG_RETURN_INT32(result); + } + + /* + *---------------------------------------------------------- + * CANONICAL FUNCTIONS + *---------------------------------------------------------- + */ + + Datum + int4range_canonical(PG_FUNCTION_ARGS) + { + RangeType *r = PG_GETARG_RANGE(0); + + RangeBound lower; + RangeBound upper; + bool empty; + + range_deserialize(r, &lower, &upper, &empty); + + if (empty) + PG_RETURN_RANGE(r); + + if (!lower.infinite && !lower.inclusive) + { + lower.val = DirectFunctionCall2(int4pl, lower.val, Int32GetDatum(1)); + lower.inclusive = true; + } + + if (!upper.infinite && upper.inclusive) + { + upper.val = DirectFunctionCall2(int4pl, upper.val, Int32GetDatum(1)); + upper.inclusive = false; + } + + PG_RETURN_RANGE(range_serialize(&lower, &upper, false)); + } + + Datum + int8range_canonical(PG_FUNCTION_ARGS) + { + RangeType *r = PG_GETARG_RANGE(0); + + RangeBound lower; + RangeBound upper; + bool empty; + + range_deserialize(r, &lower, &upper, &empty); + + if (empty) + PG_RETURN_RANGE(r); + + if (!lower.infinite && !lower.inclusive) + { + lower.val = DirectFunctionCall2(int8pl, lower.val, Int64GetDatum(1)); + lower.inclusive = true; + } + + if (!upper.infinite && upper.inclusive) + { + upper.val = DirectFunctionCall2(int8pl, upper.val, Int64GetDatum(1)); + upper.inclusive = false; + } + + PG_RETURN_RANGE(range_serialize(&lower, &upper, false)); + } + + Datum + daterange_canonical(PG_FUNCTION_ARGS) + { + RangeType *r = PG_GETARG_RANGE(0); + + RangeBound lower; + RangeBound upper; + bool empty; + + range_deserialize(r, &lower, &upper, &empty); + + if (empty) + PG_RETURN_RANGE(r); + + if (!lower.infinite && !lower.inclusive) + { + lower.val = DirectFunctionCall2(date_pli, lower.val, Int32GetDatum(1)); + lower.inclusive = true; + } + + if (!upper.infinite && upper.inclusive) + { + upper.val = DirectFunctionCall2(date_pli, upper.val, Int32GetDatum(1)); + upper.inclusive = false; + } + + PG_RETURN_RANGE(range_serialize(&lower, &upper, false)); + } + + /* + *---------------------------------------------------------- + * SUPPORT FUNCTIONS + *---------------------------------------------------------- + */ + + /* + * Serialized format is: + * + * 4 bytes: Range type Oid + * Lower boundary, if any, aligned according to subtype's typalign + * Upper boundary, if any, aligned according to subtype's typalign + * 1 byte for flags + * + * This representation is chosen to be compact when the boundary + * values need to be MAXALIGNed. A palloc chunk always starts out + * MAXALIGNed, and the first 4 bytes will be the length header (range + * types are always variable-length), then the next 4 bytes will be + * the range type Oid. That leaves the first boundary item MAXALIGNed + * without the need for padding. + * + * However, it requires a slightly odd deserialization strategy, + * because we have to read the flags byte before we know whether to + * read a boundary value. + */ + + /* + * This serializes a range, but does not canonicalize it. This should + * only be called by a canonicalization function. + */ + Datum + range_serialize(RangeBound *lower, RangeBound *upper, bool empty) + { + Datum range; + size_t msize; + Pointer ptr; + Oid subtype = get_range_subtype(lower->rngtypid); + int16 typlen = get_typlen(subtype); + char typalign = get_typalign(subtype); + char typbyval = get_typbyval(subtype); + char typstorage = get_typstorage(subtype); + char flags = 0; + + if (lower->rngtypid != upper->rngtypid) + elog(ERROR, "range types do not match"); + + if (empty) + flags |= RANGE_EMPTY; + else if (range_cmp_bounds(lower, upper) > 0) + elog(ERROR, "range lower bound must be less than or equal to range upper bound"); + + flags |= (lower->inclusive) ? RANGE_LB_INC : 0; + flags |= (lower->infinite) ? RANGE_LB_INF : 0; + flags |= (upper->inclusive) ? RANGE_UB_INC : 0; + flags |= (upper->infinite) ? RANGE_UB_INF : 0; + + msize = VARHDRSZ; + msize += sizeof(Oid); + + if (RANGE_HAS_LBOUND(flags)) + { + msize = datum_compute_size(msize, lower->val, typbyval, typalign, + typlen, typstorage); + } + + if (RANGE_HAS_UBOUND(flags)) + { + msize = datum_compute_size(msize, upper->val, typbyval, typalign, + typlen, typstorage); + } + + msize += sizeof(char); + + ptr = palloc0(msize); + range = (Datum) ptr; + + ptr += VARHDRSZ; + + memcpy(ptr, &lower->rngtypid, sizeof(Oid)); + ptr += sizeof(Oid); + + if (RANGE_HAS_LBOUND(flags)) + { + Assert(lower->lower); + ptr = datum_write(ptr, lower->val, typbyval, typalign, typlen, + typstorage); + } + + if (RANGE_HAS_UBOUND(flags)) + { + Assert(!upper->lower); + ptr = datum_write(ptr, upper->val, typbyval, typalign, typlen, + typstorage); + } + + memcpy(ptr, &flags, sizeof(char)); + ptr += sizeof(char); + + SET_VARSIZE(range, msize); + PG_RETURN_RANGE(range); + } + + void + range_deserialize(RangeType *range, RangeBound *lower, RangeBound *upper, + bool *empty) + { + Pointer ptr = VARDATA(range); + char typalign; + int16 typlen; + int16 typbyval; + char flags; + Oid rngtypid; + Oid subtype; + Datum lbound; + Datum ubound; + Pointer flags_ptr; + + memset(lower, 0, sizeof(RangeBound)); + memset(upper, 0, sizeof(RangeBound)); + + /* peek at last byte to read the flag byte */ + flags_ptr = ptr + VARSIZE(range) - VARHDRSZ - 1; + memcpy(&flags, flags_ptr, sizeof(char)); + + memcpy(&rngtypid, ptr, sizeof(Oid)); + ptr += sizeof(Oid); + + if (rngtypid == ANYRANGEOID) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot output a value of type anyrange"))); + + subtype = get_range_subtype(rngtypid); + typalign = get_typalign(subtype); + typlen = get_typlen(subtype); + typbyval = get_typbyval(subtype); + + if (RANGE_HAS_LBOUND(flags)) + { + ptr = (Pointer) att_align_pointer(ptr, typalign, typlen, ptr); + lbound = fetch_att(ptr, typbyval, typlen); + ptr = (Pointer) att_addlength_datum(ptr, typlen, PointerGetDatum(ptr)); + if (typlen == -1) + lbound = PointerGetDatum(PG_DETOAST_DATUM(lbound)); + } + else + lbound = (Datum) 0; + + if (RANGE_HAS_UBOUND(flags)) + { + ptr = (Pointer) att_align_pointer(ptr, typalign, typlen, ptr); + ubound = fetch_att(ptr, typbyval, typlen); + ptr = (Pointer) att_addlength_datum(ptr, typlen, PointerGetDatum(ptr)); + if (typlen == -1) + ubound = PointerGetDatum(PG_DETOAST_DATUM(ubound)); + } + else + ubound = (Datum) 0; + + *empty = flags & RANGE_EMPTY; + + lower->rngtypid = rngtypid; + lower->val = lbound; + lower->inclusive = flags & RANGE_LB_INC; + lower->infinite = flags & RANGE_LB_INF; + lower->lower = true; + + upper->rngtypid = rngtypid; + upper->val = ubound; + upper->inclusive = flags & RANGE_UB_INC; + upper->infinite = flags & RANGE_UB_INF; + upper->lower = false; + } + + /* + * This both serializes and caonicalizes (if applicable) the + * range. This should be used by most callers. + */ + Datum + make_range(RangeBound *lower, RangeBound *upper, bool empty) + { + Datum range; + Oid canonical = get_range_canonical(lower->rngtypid); + + if (lower->rngtypid != upper->rngtypid) + elog(ERROR, "range types do not match"); + + range = range_serialize(lower, upper, empty); + + if (OidIsValid(canonical)) + range = OidFunctionCall1(canonical, range); + + PG_RETURN_RANGE(range); + } + + int + range_cmp_bounds(RangeBound *b1, RangeBound *b2) + { + int result; + FmgrInfo flinfo; + Oid collation; + + if (b1->infinite && b2->infinite) + { + if (b1->lower == b2->lower) + return 0; + else + return (b1->lower) ? -1 : 1; + } + else if (b1->infinite && !b2->infinite) + return (b1->lower) ? -1 : 1; + else if (!b1->infinite && b2->infinite) + return (b2->lower) ? 1 : -1; + + range_subtype_cmpfn(b1->rngtypid, &flinfo, &collation); + result = DatumGetInt32(FunctionCall2Coll(&flinfo, collation, + b1->val, b2->val)); + + if (result == 0) + { + if (b1->inclusive && !b2->inclusive) + return (b2->lower) ? -1 : 1; + else if (!b1->inclusive && b2->inclusive) + return (b1->lower) ? 1 : -1; + } + + return result; + } + + RangeType * + make_empty_range(Oid rngtypid) + { + RangeBound lower; + RangeBound upper; + + memset(&lower, 0, sizeof(RangeBound)); + memset(&upper, 0, sizeof(RangeBound)); + + lower.rngtypid = rngtypid; + lower.lower = true; + upper.rngtypid = rngtypid; + upper.lower = false; + + return DatumGetRangeType(make_range(&lower, &upper, true)); + } + + /* + *---------------------------------------------------------- + * STATIC FUNCTIONS + *---------------------------------------------------------- + */ + + /* + * Parse input of the form: + * + * where + * is either "[" or "(" + * and are either plain strings or quoted strings + * is "," + * is either "]" or ")" + * + * Quoted strings preserve whitespace, and allow using special + * characters like "," and special values like "INF" ("\" is the + * escape character if you need to include a double quote or backslash + * character). Unquoted strings preserve internal whitespace, but not + * leading or trailing whitespace. + */ + static void + range_parse(const char *input_str, char *flags, char **lbound_str, + char **ubound_str) + { + int ilen = strlen(input_str); + char *lb = palloc0(ilen + 1); + char *ub = palloc0(ilen + 1); + int lidx = 0; + int lb_last = 1; + int uidx = 0; + int ub_last = 1; + bool inside_quotes = false; + bool lb_quoted = false; + bool ub_quoted = false; + bool escape = false; + char fl = 0; + RangePState pstate = RANGE_PSTATE_INIT; + int i; + + + for (i = 0; i < ilen; i++) + { + char ch = input_str[i]; + switch(pstate) + { + case RANGE_PSTATE_INIT: + if (isspace(ch)) + continue; + if(ch == '-') + { + fl = RANGE_EMPTY; + pstate = RANGE_PSTATE_DONE; + /* read the rest to make sure it's whitespace */ + continue; + } + + if (ch == '[' || ch == '(') + { + if (ch == '[') + fl |= RANGE_LB_INC; + pstate = RANGE_PSTATE_PRE_LB; + continue; + } + break; + case RANGE_PSTATE_PRE_LB: + /* remove leading whitespace */ + if (isspace(ch)) + continue; + pstate = RANGE_PSTATE_LB; + /* fall through */ + case RANGE_PSTATE_LB: + if (ch == '"' && !escape) + { + if (inside_quotes) + { + inside_quotes = false; + pstate = RANGE_PSTATE_SEP; + } + else + { + inside_quotes = true; + lb_quoted = true; + } + continue; + } + if (ch == '\\' && !escape && inside_quotes) + { + escape = true; + continue; + } + escape = false; + + if (ch != ',') + { + lb[lidx++] = ch; + /* track last significant character */ + if (inside_quotes || !isspace(ch)) + lb_last = lidx; + continue; + } + + pstate = RANGE_PSTATE_SEP; + /* fall through */ + case RANGE_PSTATE_SEP: + if (isspace(ch)) + continue; + if (ch == ',') + { + pstate = RANGE_PSTATE_PRE_UB; + continue; + } + case RANGE_PSTATE_PRE_UB: + /* remove leading whitespace */ + if (isspace(ch)) + continue; + pstate = RANGE_PSTATE_UB; + /* fall through */ + case RANGE_PSTATE_UB: + if (ch == '"' && !escape) + { + if (inside_quotes) + { + inside_quotes = false; + pstate = RANGE_PSTATE_UB_INC; + } + else + { + inside_quotes = true; + lb_quoted = true; + } + continue; + } + if (ch == '\\' && !escape && inside_quotes) + { + escape = true; + continue; + } + escape = false; + + if (ch != ']' && ch != ')') + { + ub[uidx++] = ch; + /* track last significant character */ + if (inside_quotes || !isspace(ch)) + ub_last = uidx; + continue; + } + + pstate = RANGE_PSTATE_UB_INC; + /* fall through */ + case RANGE_PSTATE_UB_INC: + if (isspace(ch)) + continue; + if (ch == ']' || ch == ')') + { + if (ch == ']') + fl |= RANGE_UB_INC; + pstate = RANGE_PSTATE_DONE; + continue; + } + break; + case RANGE_PSTATE_DONE: + if (isspace(ch)) + continue; + break; + } + elog(ERROR, "syntax error on range input \"%s\", character %d", + input_str, i); + } + + /* remove trailing whitespace */ + lb[lb_last] = '\0'; + ub[ub_last] = '\0'; + + if (pstate != RANGE_PSTATE_DONE) + elog(ERROR, "syntax error on range input: unexpected end of input"); + + if (fl & RANGE_EMPTY) + fl = RANGE_EMPTY; + + if (!lb_quoted && strncmp(lb, "NULL", ilen) == 0) + elog(ERROR, "NULL range boundaries are not supported"); + if (!ub_quoted && strncmp(ub, "NULL", ilen) == 0) + elog(ERROR, "NULL range boundaries are not supported"); + if (!lb_quoted && strncmp(lb, "-INF", ilen) == 0) + fl |= RANGE_LB_INF; + if (!ub_quoted && strncmp(ub, "INF", ilen) == 0) + fl |= RANGE_UB_INF; + + if (!RANGE_HAS_LBOUND(fl)) + { + lb = NULL; + fl &= ~RANGE_LB_INC; + } + + if (!RANGE_HAS_UBOUND(fl)) + { + ub = NULL; + fl &= ~RANGE_UB_INC; + } + + *lbound_str = lb; + *ubound_str = ub; + *flags = fl; + + return; + } + + static char * + range_deparse(char flags, char *lbound_str, char *ubound_str) + { + StringInfo str = makeStringInfo(); + char lb_c; + char ub_c; + char *lb_str; + char *ub_str; + + if (flags & RANGE_EMPTY) + return pstrdup("-"); + + lb_c = (flags & RANGE_LB_INC) ? '[' : '('; + ub_c = (flags & RANGE_UB_INC) ? ']' : ')'; + + lb_str = lbound_str; + ub_str = ubound_str; + + if (!RANGE_HAS_LBOUND(flags)) + lb_str = (flags & RANGE_LB_NULL) ? "NULL" : "-INF"; + if (!RANGE_HAS_UBOUND(flags)) + ub_str = (flags & RANGE_UB_NULL) ? "NULL" : "INF"; + + appendStringInfo(str, "%c %s, %s %c", lb_c, lb_str, ub_str, ub_c); + + return str->data; + } + + static Datum + range_make2(PG_FUNCTION_ARGS) + { + Datum arg1 = PG_GETARG_DATUM(0); + Datum arg2 = PG_GETARG_DATUM(1); + Oid subtype = get_fn_expr_argtype(fcinfo->flinfo,0); + Oid rngtypid = get_range_from_subtype(subtype); + RangeType *range; + RangeBound lower; + RangeBound upper; + + memset(&lower, 0, sizeof(RangeBound)); + memset(&upper, 0, sizeof(RangeBound)); + + switch(fcinfo->flinfo->fn_oid) + { + case F_RANGE__: + break; + case F_RANGE_I: + upper.inclusive = true; + break; + case F_RANGE: + case F_RANGEI_: + lower.inclusive = true; + break; + case F_RANGEII: + lower.inclusive = true; + upper.inclusive = true; + break; + } + + if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) + elog(ERROR, "NULL range boundaries are not supported"); + + lower.rngtypid = rngtypid; + lower.lower = true; + lower.val = arg1; + + upper.rngtypid = rngtypid; + upper.lower = false; + upper.val = arg2; + + range = DatumGetRangeType(make_range(&lower, &upper, false)); + + PG_RETURN_RANGE(range); + } + + static bool + range_contains_internal(RangeType *r1, RangeType *r2) + { + RangeBound lower1; + RangeBound upper1; + bool empty1; + RangeBound lower2; + RangeBound upper2; + bool empty2; + + range_deserialize(r1, &lower1, &upper1, &empty1); + range_deserialize(r2, &lower2, &upper2, &empty2); + + if (lower1.rngtypid != upper1.rngtypid || + lower1.rngtypid != lower2.rngtypid || + lower1.rngtypid != upper2.rngtypid) + elog(ERROR, "range types do not match"); + + if (empty2) + return true; + else if (empty1) + return false; + + if (range_cmp_bounds(&lower1, &lower2) > 0) + return false; + if (range_cmp_bounds(&upper1, &upper2) < 0) + return false; + + return true; + } + + /* + * datum_compute_size() and datum_write() are modeled after + * heap_compute_data_size() and heap_fill_tuple(). + */ + + static Size + datum_compute_size(Size data_length, Datum val, bool typbyval, char typalign, + int16 typlen, char typstorage) + { + if (TYPE_IS_PACKABLE(typlen, typstorage) && + VARATT_CAN_MAKE_SHORT(DatumGetPointer(val))) + { + /* + * we're anticipating converting to a short varlena header, so + * adjust length and don't count any alignment + */ + data_length += VARATT_CONVERTED_SHORT_SIZE(DatumGetPointer(val)); + } + else + { + data_length = att_align_datum(data_length, typalign, typlen, val); + data_length = att_addlength_datum(data_length, typlen, val); + } + + return data_length; + } + + static Pointer + datum_write(Pointer ptr, Datum datum, bool typbyval, char typalign, + int16 typlen, char typstorage) + { + Size data_length; + + if (typbyval) + { + /* pass-by-value */ + ptr = (char *) att_align_nominal(ptr, typalign); + store_att_byval(ptr, datum, typlen); + data_length = typlen; + } + else if (typlen == -1) + { + /* varlena */ + Pointer val = DatumGetPointer(datum); + + if (VARATT_IS_EXTERNAL(val)) + { + /* no alignment, since it's short by definition */ + data_length = VARSIZE_EXTERNAL(val); + memcpy(ptr, val, data_length); + } + else if (VARATT_IS_SHORT(val)) + { + /* no alignment for short varlenas */ + data_length = VARSIZE_SHORT(val); + memcpy(ptr, val, data_length); + } + else if (TYPE_IS_PACKABLE(typlen, typstorage) && + VARATT_CAN_MAKE_SHORT(val)) + { + /* convert to short varlena -- no alignment */ + data_length = VARATT_CONVERTED_SHORT_SIZE(val); + SET_VARSIZE_SHORT(ptr, data_length); + memcpy(ptr + 1, VARDATA(val), data_length - 1); + } + else + { + /* full 4-byte header varlena */ + ptr = (char *) att_align_nominal(ptr, typalign); + data_length = VARSIZE(val); + memcpy(ptr, val, data_length); + } + } + else if (typlen == -2) + { + /* cstring ... never needs alignment */ + Assert(typalign == 'c'); + data_length = strlen(DatumGetCString(datum)) + 1; + memcpy(ptr, DatumGetPointer(datum), data_length); + } + else + { + /* fixed-length pass-by-reference */ + ptr = (char *) att_align_nominal(ptr, typalign); + Assert(typlen > 0); + data_length = typlen; + memcpy(ptr, DatumGetPointer(datum), data_length); + } + + ptr += data_length; + + return ptr; + } + + static void + range_subtype_cmpfn(Oid rngtypid, FmgrInfo *flinfo, Oid *collation) + { + Oid opclassId = get_range_sub_opclass(rngtypid); + Oid opfamilyId; + Oid cmpFnOid; + Oid subtype; + HeapTuple tuple; + Form_pg_opclass pg_opclass; + + *collation = get_range_collation(rngtypid); + + tuple = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassId)); + if (!HeapTupleIsValid(tuple)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("operator class with OID %u does not exist", + opclassId))); + pg_opclass = (Form_pg_opclass) GETSTRUCT(tuple); + opfamilyId = pg_opclass->opcfamily; + ReleaseSysCache(tuple); + + subtype = getBaseType(get_range_subtype(rngtypid)); + cmpFnOid = get_opfamily_proc(opfamilyId, subtype, subtype, BTORDER_PROC); + + fmgr_info(cmpFnOid, flinfo); + } *** /dev/null --- b/src/backend/utils/adt/rangetypes_gist.c *************** *** 0 **** --- 1,537 ---- + /*------------------------------------------------------------------------- + * + * rangetypes_gist.c + * GiST support for range types. + * + * Copyright (c) 2006-2011, PostgreSQL Global Development Group + * + * + * IDENTIFICATION + * src/backend/utils/adt/rangetypes_gist.c + * + *------------------------------------------------------------------------- + */ + #include "postgres.h" + + #include "access/gist.h" + #include "access/skey.h" + #include "utils/lsyscache.h" + #include "utils/rangetypes.h" + + #define RANGESTRAT_EQ 1 + #define RANGESTRAT_NE 2 + #define RANGESTRAT_OVERLAPS 3 + #define RANGESTRAT_CONTAINS_ELEM 4 + #define RANGESTRAT_ELEM_CONTAINED_BY 5 + #define RANGESTRAT_CONTAINS 6 + #define RANGESTRAT_CONTAINED_BY 7 + #define RANGESTRAT_BEFORE 8 + #define RANGESTRAT_AFTER 9 + #define RANGESTRAT_OVERLEFT 10 + #define RANGESTRAT_OVERRIGHT 11 + #define RANGESTRAT_ADJACENT 12 + + static RangeType *range_super_union(RangeType *r1, RangeType *r2); + static bool range_gist_consistent_int(StrategyNumber strategy, RangeType *key, + RangeType *query); + static bool range_gist_consistent_leaf(StrategyNumber strategy, RangeType *key, + RangeType *query); + static int sort_item_cmp(const void *a, const void *b); + + /* + * Auxiliary structure for picksplit method. + */ + typedef struct + { + int index; + RangeType *data; + } PickSplitSortItem; + + + /* GiST support */ + Datum + range_gist_consistent(PG_FUNCTION_ARGS) + { + GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); + Datum dquery = PG_GETARG_DATUM(1); + StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2); + /* Oid subtype = PG_GETARG_OID(3); */ + bool *recheck = (bool *) PG_GETARG_POINTER(4); + RangeType *key = DatumGetRangeType(entry->key); + RangeType *query; + + RangeBound lower; + RangeBound upper; + bool empty; + Oid rngtypid; + + *recheck = false; + range_deserialize(key, &lower, &upper, &empty); + rngtypid = lower.rngtypid; + + switch(strategy) + { + RangeBound lower; + RangeBound upper; + + case RANGESTRAT_CONTAINS_ELEM: + case RANGESTRAT_ELEM_CONTAINED_BY: + lower.rngtypid = rngtypid; + lower.inclusive = true; + lower.val = dquery; + lower.lower = true; + lower.infinite = false; + upper.rngtypid = rngtypid; + upper.inclusive = true; + upper.val = dquery; + upper.lower = false; + upper.infinite = false; + query = DatumGetRangeType( + make_range(&lower, &upper, false)); + break; + default: + query = DatumGetRangeType(dquery); + break; + } + + if(GIST_LEAF(entry)) + PG_RETURN_BOOL(range_gist_consistent_leaf(strategy, key, query)); + else + PG_RETURN_BOOL(range_gist_consistent_int(strategy, key, query)); + } + + Datum + range_gist_union(PG_FUNCTION_ARGS) + { + GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0); + GISTENTRY *ent = entryvec->vector; + RangeType *result_range; + int i; + + result_range = DatumGetRangeType(ent[0].key); + + for (i = 1; i < entryvec->n; i++) + { + result_range = range_super_union(result_range, + DatumGetRangeType(ent[i].key)); + } + + PG_RETURN_RANGE(result_range); + } + + Datum + range_gist_compress(PG_FUNCTION_ARGS) + { + GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); + PG_RETURN_POINTER(entry); + } + + Datum + range_gist_decompress(PG_FUNCTION_ARGS) + { + GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); + PG_RETURN_POINTER(entry); + } + + Datum + range_gist_penalty(PG_FUNCTION_ARGS) + { + GISTENTRY *origentry = (GISTENTRY *) PG_GETARG_POINTER(0); + GISTENTRY *newentry = (GISTENTRY *) PG_GETARG_POINTER(1); + float *penalty = (float *) PG_GETARG_POINTER(2); + RangeType *orig = DatumGetRangeType(origentry->key); + RangeType *new = DatumGetRangeType(newentry->key); + RangeType *s_union = range_super_union(orig, new); + + regproc subtype_float; + RangeBound lower1, lower2; + RangeBound upper1, upper2; + bool empty1, empty2; + float length1, length2; + + range_deserialize(orig, &lower1, &upper1, &empty1); + range_deserialize(s_union, &lower2, &upper2, &empty2); + + subtype_float = get_range_subtype_float(lower1.rngtypid); + + if (empty1) + length1 = 0.0; + else if (lower1.infinite || upper1.infinite) + length1 = 1.0/0.0; + else if (OidIsValid(subtype_float)) + { + double l = DatumGetFloat8(OidFunctionCall1(subtype_float, lower1.val)); + double u = DatumGetFloat8(OidFunctionCall1(subtype_float, upper1.val)); + length1 = u - l; + } + else + length1 = 1.0; + + if (empty2) + length2 = 0.0; + else if (lower2.infinite || upper2.infinite) + length2 = 1.0/0.0; + else if (OidIsValid(subtype_float)) + { + double l = DatumGetFloat8(OidFunctionCall1(subtype_float, lower2.val)); + double u = DatumGetFloat8(OidFunctionCall1(subtype_float, upper2.val)); + length2 = u - l; + } + else + length2 = 1.0; + + *penalty = (float) (length2 - length1); + PG_RETURN_POINTER(penalty); + } + + /* + * The GiST PickSplit method for ranges + * Algorithm based on sorting. Incoming array of periods is sorting using + * period_compare function. After that first half of periods goes to the left + * datum, and the second half of periods goes to the right datum. + */ + Datum + range_gist_picksplit(PG_FUNCTION_ARGS) + { + GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0); + GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1); + OffsetNumber i; + RangeType *pred_left; + RangeType *pred_right; + PickSplitSortItem *sortItems; + int nbytes; + OffsetNumber split_idx; + OffsetNumber *left; + OffsetNumber *right; + OffsetNumber maxoff; + + maxoff = entryvec->n - 1; + nbytes = (maxoff + 1) * sizeof(OffsetNumber); + sortItems = (PickSplitSortItem *)palloc(maxoff * sizeof(PickSplitSortItem)); + v->spl_left = (OffsetNumber *) palloc(nbytes); + v->spl_right = (OffsetNumber *) palloc(nbytes); + + /* + * Preparing auxiliary array and sorting. + */ + for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) + { + sortItems[i - 1].index = i; + sortItems[i - 1].data = DatumGetRangeType(entryvec->vector[i].key); + } + qsort(sortItems, maxoff, sizeof(PickSplitSortItem), sort_item_cmp); + split_idx = maxoff / 2; + + left = v->spl_left; + v->spl_nleft = 0; + right = v->spl_right; + v->spl_nright = 0; + + /* + * First half of segs goes to the left datum. + */ + pred_left = DatumGetRangeType(sortItems[0].data); + *left++ = sortItems[0].index; + v->spl_nleft++; + for (i = 1; i < split_idx; i++) + { + pred_left = range_super_union(pred_left, + DatumGetRangeType(sortItems[i].data)); + *left++ = sortItems[i].index; + v->spl_nleft++; + } + + /* + * Second half of segs goes to the right datum. + */ + pred_right = DatumGetRangeType(sortItems[split_idx].data); + *right++ = sortItems[split_idx].index; + v->spl_nright++; + for (i = split_idx + 1; i < maxoff; i++) + { + pred_right = range_super_union(pred_right, + DatumGetRangeType(sortItems[i].data)); + *right++ = sortItems[i].index; + v->spl_nright++; + } + + *left = *right = FirstOffsetNumber; /* sentinel value, see dosplit() */ + + v->spl_ldatum = RangeTypeGetDatum(pred_left); + v->spl_rdatum = RangeTypeGetDatum(pred_right); + + PG_RETURN_POINTER(v); + } + + Datum + range_gist_same(PG_FUNCTION_ARGS) + { + Datum r1 = PG_GETARG_DATUM(0); + Datum r2 = PG_GETARG_DATUM(1); + bool *result = (bool *) PG_GETARG_POINTER(2); + + *result = DatumGetBool(DirectFunctionCall2(range_eq, r1, r2)); + PG_RETURN_POINTER(result); + } + + /* + *---------------------------------------------------------- + * STATIC FUNCTIONS + *---------------------------------------------------------- + */ + + /* return the smallest range that contains r1 and r2 */ + static RangeType * + range_super_union(RangeType *r1, RangeType *r2) + { + RangeBound lower1, lower2; + RangeBound upper1, upper2; + bool empty1, empty2; + RangeBound *result_lower; + RangeBound *result_upper; + + range_deserialize(r1, &lower1, &upper1, &empty1); + range_deserialize(r2, &lower2, &upper2, &empty2); + + if (empty1) + return r2; + if (empty2) + return r1; + + if (range_cmp_bounds(&lower1, &lower2) <= 0) + result_lower = &lower1; + else + result_lower = &lower2; + + if (range_cmp_bounds(&upper1, &upper2) >= 0) + result_upper = &upper1; + else + result_upper = &upper2; + + /* optimization to avoid constructing a new range */ + if (result_lower == &lower1 && result_upper == &upper1) + return r1; + if (result_lower == &lower2 && result_upper == &upper2) + return r2; + + return DatumGetRangeType(make_range(result_lower, result_upper, false)); + } + + static bool + range_gist_consistent_int(StrategyNumber strategy, RangeType *key, + RangeType *query) + { + Datum (*proc)(PG_FUNCTION_ARGS) = NULL; + + RangeBound lower1, lower2; + RangeBound upper1, upper2; + bool empty1, empty2; + + bool retval; + bool negate = false; + + range_deserialize(key, &lower1, &upper1, &empty1); + range_deserialize(query, &lower2, &upper2, &empty2); + + switch (strategy) + { + case RANGESTRAT_EQ: + proc = range_contains; + break; + case RANGESTRAT_NE: + return true; + break; + case RANGESTRAT_OVERLAPS: + proc = range_overlaps; + break; + case RANGESTRAT_CONTAINS_ELEM: + case RANGESTRAT_CONTAINS: + proc = range_contains; + break; + case RANGESTRAT_ELEM_CONTAINED_BY: + case RANGESTRAT_CONTAINED_BY: + return true; + break; + case RANGESTRAT_BEFORE: + if (empty1) + return false; + proc = range_overright; + negate = true; + break; + case RANGESTRAT_AFTER: + if (empty1) + return false; + proc = range_overleft; + negate = true; + break; + case RANGESTRAT_OVERLEFT: + if (empty1) + return false; + proc = range_after; + negate = true; + break; + case RANGESTRAT_OVERRIGHT: + if (empty1) + return false; + proc = range_before; + negate = true; + break; + case RANGESTRAT_ADJACENT: + if (empty1 || empty2) + return false; + if (DatumGetBool( + DirectFunctionCall2(range_adjacent, + RangeTypeGetDatum(key), + RangeTypeGetDatum(query)))) + return true; + proc = range_overlaps; + break; + } + + retval = DatumGetBool(DirectFunctionCall2(proc, RangeTypeGetDatum(key), + RangeTypeGetDatum(query))); + + if (negate) + retval = !retval; + + PG_RETURN_BOOL(retval); + } + + static bool + range_gist_consistent_leaf(StrategyNumber strategy, RangeType *key, + RangeType *query) + { + Datum (*proc)(PG_FUNCTION_ARGS) = NULL; + + RangeBound lower1, lower2; + RangeBound upper1, upper2; + bool empty1, empty2; + + range_deserialize(key, &lower1, &upper1, &empty1); + range_deserialize(query, &lower2, &upper2, &empty2); + + switch (strategy) + { + case RANGESTRAT_EQ: + proc = range_eq; + break; + case RANGESTRAT_NE: + proc = range_ne; + break; + case RANGESTRAT_OVERLAPS: + proc = range_overlaps; + break; + case RANGESTRAT_CONTAINS_ELEM: + case RANGESTRAT_CONTAINS: + proc = range_contains; + break; + case RANGESTRAT_ELEM_CONTAINED_BY: + case RANGESTRAT_CONTAINED_BY: + proc = range_contained_by; + break; + case RANGESTRAT_BEFORE: + if (empty1 || empty2) + return false; + proc = range_before; + break; + case RANGESTRAT_AFTER: + if (empty1 || empty2) + return false; + proc = range_after; + break; + case RANGESTRAT_OVERLEFT: + if (empty1 || empty2) + return false; + proc = range_overleft; + break; + case RANGESTRAT_OVERRIGHT: + if (empty1 || empty2) + return false; + proc = range_overright; + break; + case RANGESTRAT_ADJACENT: + if (empty1 || empty2) + return false; + proc = range_adjacent; + break; + } + + return DatumGetBool(DirectFunctionCall2(proc, RangeTypeGetDatum(key), + RangeTypeGetDatum(query))); + } + + /* + * Compare function for PickSplitSortItem. This is actually the + * interesting part of the picksplit algorithm. + * + * We want to separate out empty ranges, bounded ranges, and unbounded + * ranges. We assume that "contains" and "overlaps" are the most + * important queries, so empty ranges will rarely match and unbounded + * ranges frequently will. Bounded ranges should be in the middle. + * + * Empty ranges we push all the way to the left, then bounded ranges + * (sorted on lower bound, then upper), then ranges with no lower + * bound, then ranges with no upper bound; and finally, ranges with no + * upper or lower bound all the way to the right. + */ + static int + sort_item_cmp(const void *a, const void *b) + { + PickSplitSortItem *i1 = (PickSplitSortItem *)a; + PickSplitSortItem *i2 = (PickSplitSortItem *)b; + RangeType *r1 = i1->data; + RangeType *r2 = i2->data; + + RangeBound lower1, lower2; + RangeBound upper1, upper2; + bool empty1, empty2; + + int cmp; + + range_deserialize(r1, &lower1, &upper1, &empty1); + range_deserialize(r2, &lower2, &upper2, &empty2); + + if (empty1 || empty2) + { + if (empty1 && empty2) + return 0; + else if (empty1) + return -1; + else if (empty2) + return 1; + else + Assert(false); + } + + /* + * If both lower or both upper bounds are infinite, we sort by + * ascending range size. That means that if both upper bounds are + * infinite, we sort by the lower bound _descending_. That creates + * a slightly odd total order, but keeps the pages with very + * unselective predicates grouped more closely together on the + * right. + */ + if (lower1.infinite || upper1.infinite || + lower2.infinite || upper2.infinite) + { + if (lower1.infinite && lower2.infinite) + return range_cmp_bounds(&upper1, &upper2); + else if (lower1.infinite) + return -1; + else if (lower2.infinite) + return 1; + else if (upper1.infinite && upper2.infinite) + return -1 * range_cmp_bounds(&lower1, &lower2); + else if (upper1.infinite) + return 1; + else if (upper2.infinite) + return -1; + else + Assert(false); + } + + if ((cmp = range_cmp_bounds(&lower1, &lower2)) != 0) + return cmp; + + return range_cmp_bounds(&upper1, &upper2); + } *** a/src/backend/utils/adt/timestamp.c --- b/src/backend/utils/adt/timestamp.c *************** *** 4630,4635 **** timestamptz_zone(PG_FUNCTION_ARGS) --- 4630,4650 ---- PG_RETURN_TIMESTAMP(result); } + Datum + timestamp_float8(PG_FUNCTION_ARGS) + { + Timestamp timestamp = PG_GETARG_TIMESTAMP(0); + float8 result; + + #ifdef HAVE_INT64_TIMESTAMP + result = ((float8) timestamp) / USECS_PER_SEC; + #else + result = timestamp; + #endif + + PG_RETURN_FLOAT8(result); + } + /* timestamptz_izone() * Encode timestamp with time zone type with specified time interval as time zone. * Returns a timestamp without time zone. *** a/src/backend/utils/cache/lsyscache.c --- b/src/backend/utils/cache/lsyscache.c *************** *** 26,31 **** --- 26,32 ---- #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" #include "catalog/pg_proc.h" + #include "catalog/pg_range.h" #include "catalog/pg_statistic.h" #include "catalog/pg_type.h" #include "miscadmin.h" *************** *** 1978,1984 **** get_type_io_data(Oid typid, ReleaseSysCache(typeTuple); } - #ifdef NOT_USED char get_typalign(Oid typid) { --- 1979,1984 ---- *************** *** 1997,2003 **** get_typalign(Oid typid) else return 'i'; } - #endif char get_typstorage(Oid typid) --- 1997,2002 ---- *************** *** 2251,2256 **** type_is_enum(Oid typid) --- 2250,2265 ---- } /* + * type_is_range + * Returns true if the given type is an range type. + */ + bool + type_is_range(Oid typid) + { + return (get_typtype(typid) == TYPTYPE_RANGE); + } + + /* * get_type_category_preferred * * Given the type OID, fetch its category and preferred-type status. *************** *** 2855,2857 **** get_namespace_name(Oid nspid) --- 2864,2980 ---- else return NULL; } + + Oid + get_range_subtype(Oid rangeOid) + { + HeapTuple tp; + + tp = SearchSysCache1(RANGETYPE, ObjectIdGetDatum(rangeOid)); + if (HeapTupleIsValid(tp)) + { + Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp); + Oid result; + + result = rngtup->rngsubtype; + ReleaseSysCache(tp); + return result; + } + else + return InvalidOid; + } + + Oid + get_range_collation(Oid rangeOid) + { + HeapTuple tp; + + tp = SearchSysCache1(RANGETYPE, ObjectIdGetDatum(rangeOid)); + if (HeapTupleIsValid(tp)) + { + Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp); + Oid result; + + result = rngtup->rngcollation; + ReleaseSysCache(tp); + return result; + } + else + return InvalidOid; + } + + Oid + get_range_from_subtype(Oid subtypeOid) + { + HeapTuple tp; + + tp = SearchSysCache1(RANGESUBTYPE, ObjectIdGetDatum(subtypeOid)); + if (HeapTupleIsValid(tp)) + { + Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp); + Oid result; + + result = rngtup->rngtypid; + ReleaseSysCache(tp); + return result; + } + else + return InvalidOid; + } + + Oid + get_range_sub_opclass(Oid rangeOid) + { + HeapTuple tp; + + tp = SearchSysCache1(RANGETYPE, ObjectIdGetDatum(rangeOid)); + if (HeapTupleIsValid(tp)) + { + Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp); + Oid result; + + result = rngtup->rngsubopc; + ReleaseSysCache(tp); + return result; + } + else + return InvalidOid; + } + + Oid + get_range_subtype_float(Oid rangeOid) + { + HeapTuple tp; + + tp = SearchSysCache1(RANGETYPE, ObjectIdGetDatum(rangeOid)); + if (HeapTupleIsValid(tp)) + { + Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp); + Oid result; + + result = rngtup->rngsubfloat; + ReleaseSysCache(tp); + return result; + } + else + return InvalidOid; + } + + Oid + get_range_canonical(Oid rangeOid) + { + HeapTuple tp; + + tp = SearchSysCache1(RANGETYPE, ObjectIdGetDatum(rangeOid)); + if (HeapTupleIsValid(tp)) + { + Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp); + Oid result; + + result = rngtup->rngcanonical; + ReleaseSysCache(tp); + return result; + } + else + return InvalidOid; + } *** a/src/backend/utils/cache/syscache.c --- b/src/backend/utils/cache/syscache.c *************** *** 43,48 **** --- 43,49 ---- #include "catalog/pg_operator.h" #include "catalog/pg_opfamily.h" #include "catalog/pg_proc.h" + #include "catalog/pg_range.h" #include "catalog/pg_rewrite.h" #include "catalog/pg_statistic.h" #include "catalog/pg_tablespace.h" *************** *** 554,559 **** static const struct cachedesc cacheinfo[] = { --- 555,582 ---- }, 2048 }, + {RangeRelationId, /* RANGETYPE */ + RangeTypidIndexId, + 1, + { + Anum_pg_range_rngtypid, + 0, + 0, + 0 + }, + 1024 + }, + {RangeRelationId, /* RANGESUBTYPE */ + RangeSubtypeIndexId, + 1, + { + Anum_pg_range_rngsubtype, + 0, + 0, + 0 + }, + 1024 + }, {RelationRelationId, /* RELNAMENSP */ ClassNameNspIndexId, 2, *** a/src/backend/utils/fmgr/funcapi.c --- b/src/backend/utils/fmgr/funcapi.c *************** *** 408,417 **** resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args, --- 408,419 ---- int nargs = declared_args->dim1; bool have_anyelement_result = false; bool have_anyarray_result = false; + bool have_anyrange_result = false; bool have_anynonarray = false; bool have_anyenum = false; Oid anyelement_type = InvalidOid; Oid anyarray_type = InvalidOid; + Oid anyrange_type = InvalidOid; Oid anycollation; int i; *************** *** 434,439 **** resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args, --- 436,443 ---- have_anyelement_result = true; have_anyenum = true; break; + case ANYRANGEOID: + have_anyrange_result = true; default: break; } *************** *** 462,467 **** resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args, --- 466,475 ---- if (!OidIsValid(anyarray_type)) anyarray_type = get_call_expr_argtype(call_expr, i); break; + case ANYRANGEOID: + if (!OidIsValid(anyrange_type)) + anyrange_type = get_call_expr_argtype(call_expr, i); + break; default: break; } *************** *** 481,486 **** resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args, --- 489,503 ---- anyelement_type, ANYELEMENTOID); + if (have_anyelement_result && !OidIsValid(anyelement_type)) + anyelement_type = resolve_generic_type(ANYELEMENTOID, + anyrange_type, + ANYRANGEOID); + if (have_anyrange_result && !OidIsValid(anyrange_type)) + anyrange_type = resolve_generic_type(ANYRANGEOID, + anyelement_type, + ANYELEMENTOID); + /* Enforce ANYNONARRAY if needed */ if (have_anynonarray && type_is_array(anyelement_type)) return false; *************** *** 530,535 **** resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args, --- 547,559 ---- 0); TupleDescInitEntryCollation(tupdesc, i + 1, anycollation); break; + case ANYRANGEOID: + TupleDescInitEntry(tupdesc, i + 1, + NameStr(tupdesc->attrs[i]->attname), + anyrange_type, + -1, + 0); + break; default: break; } *************** *** 569,574 **** resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes, --- 593,599 ---- case ANYELEMENTOID: case ANYNONARRAYOID: case ANYENUMOID: + case ANYRANGEOID: if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE) have_anyelement_result = true; else *************** *** 633,638 **** resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes, --- 658,664 ---- case ANYELEMENTOID: case ANYNONARRAYOID: case ANYENUMOID: + case ANYRANGEOID: argtypes[i] = anyelement_type; break; case ANYARRAYOID: *** a/src/bin/pg_dump/pg_dump.c --- b/src/bin/pg_dump/pg_dump.c *************** *** 167,172 **** static void dumpExtension(Archive *fout, ExtensionInfo *extinfo); --- 167,173 ---- static void dumpType(Archive *fout, TypeInfo *tyinfo); static void dumpBaseType(Archive *fout, TypeInfo *tyinfo); static void dumpEnumType(Archive *fout, TypeInfo *tyinfo); + static void dumpRangeType(Archive *fout, TypeInfo *tyinfo); static void dumpDomain(Archive *fout, TypeInfo *tyinfo); static void dumpCompositeType(Archive *fout, TypeInfo *tyinfo); static void dumpCompositeTypeColComments(Archive *fout, TypeInfo *tyinfo); *************** *** 7297,7302 **** dumpType(Archive *fout, TypeInfo *tyinfo) --- 7298,7305 ---- dumpCompositeType(fout, tyinfo); else if (tyinfo->typtype == TYPTYPE_ENUM) dumpEnumType(fout, tyinfo); + else if (tyinfo->typtype == TYPTYPE_RANGE) + dumpRangeType(fout, tyinfo); } /* *************** *** 7421,7426 **** dumpEnumType(Archive *fout, TypeInfo *tyinfo) --- 7424,7512 ---- } /* + * dumpEnumType + * writes out to fout the queries to recreate a user-defined enum type + */ + static void + dumpRangeType(Archive *fout, TypeInfo *tyinfo) + { + PQExpBuffer q = createPQExpBuffer(); + PQExpBuffer delq = createPQExpBuffer(); + PQExpBuffer query = createPQExpBuffer(); + PGresult *res; + int num; + + char *canonical; + Oid canonicalOid; + + /* Set proper schema search path */ + selectSourceSchema("pg_catalog"); + + appendPQExpBuffer(query, "SELECT rngtypid, " + "format_type(rngsubtype, NULL) as rngsubtype, " + "rngsubcmp, rngcanonical " + "FROM pg_catalog.pg_range " + "WHERE rngtypid = '%u'", + tyinfo->dobj.catId.oid); + + res = PQexec(g_conn, query->data); + check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK); + + num = PQntuples(res); + + /* + * DROP must be fully qualified in case same name appears in pg_catalog. + * CASCADE shouldn't be required here as for normal types since the I/O + * functions are generic and do not get dropped. + */ + appendPQExpBuffer(delq, "DROP TYPE %s.", + fmtId(tyinfo->dobj.namespace->dobj.name)); + appendPQExpBuffer(delq, "%s;\n", + fmtId(tyinfo->dobj.name)); + + appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (", + fmtId(tyinfo->dobj.name)); + + appendPQExpBuffer(q, "\n SUBTYPE = %s", + PQgetvalue(res, 0, PQfnumber(res, "rngsubtype"))); + appendPQExpBuffer(q, ",\n SUBTYPE_CMP = %s", + PQgetvalue(res, 0, PQfnumber(res, "rngsubcmp"))); + + canonical = PQgetvalue(res, 0, PQfnumber(res, "rngcanonical")); + canonicalOid = atooid(canonical); + if (OidIsValid(canonicalOid)) + appendPQExpBuffer(q, ",\n CANONICAL = %s", canonical); + + appendPQExpBuffer(q, "\n);\n"); + + ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId, + tyinfo->dobj.name, + tyinfo->dobj.namespace->dobj.name, + NULL, + tyinfo->rolname, false, + "TYPE", SECTION_PRE_DATA, + q->data, delq->data, NULL, + tyinfo->dobj.dependencies, tyinfo->dobj.nDeps, + NULL, NULL); + + /* Dump Type Comments and Security Labels */ + resetPQExpBuffer(q); + + appendPQExpBuffer(q, "TYPE %s", fmtId(tyinfo->dobj.name)); + dumpComment(fout, q->data, + tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, + tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); + dumpSecLabel(fout, q->data, + tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, + tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); + + PQclear(res); + destroyPQExpBuffer(q); + destroyPQExpBuffer(delq); + destroyPQExpBuffer(query); + } + + /* * dumpBaseType * writes out to fout the queries to recreate a user-defined base type */ *** a/src/include/catalog/catversion.h --- b/src/include/catalog/catversion.h *************** *** 53,58 **** */ /* yyyymmddN */ ! #define CATALOG_VERSION_NO 201108051 #endif --- 53,59 ---- */ /* yyyymmddN */ ! /* COMMITTER: please set appropriately */ ! #define CATALOG_VERSION_NO 201111111 #endif *** a/src/include/catalog/indexing.h --- b/src/include/catalog/indexing.h *************** *** 303,308 **** DECLARE_UNIQUE_INDEX(pg_extension_oid_index, 3080, on pg_extension using btree(o --- 303,313 ---- DECLARE_UNIQUE_INDEX(pg_extension_name_index, 3081, on pg_extension using btree(extname name_ops)); #define ExtensionNameIndexId 3081 + DECLARE_UNIQUE_INDEX(pg_range_rgntypid_index, 3542, on pg_range using btree(rngtypid oid_ops)); + #define RangeTypidIndexId 3542 + DECLARE_UNIQUE_INDEX(pg_range_rgnsubtype_index, 3543, on pg_range using btree(rngsubtype oid_ops)); + #define RangeSubtypeIndexId 3543 + /* last step of initialization script: build the indexes declared above */ BUILD_INDICES *** a/src/include/catalog/pg_amop.h --- b/src/include/catalog/pg_amop.h *************** *** 709,712 **** DATA(insert ( 3683 3615 3615 5 s 3679 403 0 )); --- 709,742 ---- DATA(insert ( 3702 3615 3615 7 s 3693 783 0 )); DATA(insert ( 3702 3615 3615 8 s 3694 783 0 )); + /* + * btree range_ops + */ + DATA(insert ( 3901 3831 3831 1 s 3884 403 0 )); + DATA(insert ( 3901 3831 3831 2 s 3885 403 0 )); + DATA(insert ( 3901 3831 3831 3 s 3882 403 0 )); + DATA(insert ( 3901 3831 3831 4 s 3886 403 0 )); + DATA(insert ( 3901 3831 3831 5 s 3887 403 0 )); + + /* + * hash range_ops + */ + DATA(insert ( 3903 3831 3831 1 s 3882 405 0 )); + + /* + * GiST range_ops + */ + DATA(insert ( 3919 3831 3831 1 s 3882 783 0 )); + DATA(insert ( 3919 3831 3831 2 s 3883 783 0 )); + DATA(insert ( 3919 3831 3831 3 s 3888 783 0 )); + DATA(insert ( 3919 3831 2776 4 s 3889 783 0 )); + DATA(insert ( 3919 2776 3831 5 s 3891 783 0 )); + DATA(insert ( 3919 3831 3831 6 s 3890 783 0 )); + DATA(insert ( 3919 3831 3831 7 s 3892 783 0 )); + DATA(insert ( 3919 3831 3831 8 s 3893 783 0 )); + DATA(insert ( 3919 3831 3831 9 s 3894 783 0 )); + DATA(insert ( 3919 3831 3831 10 s 3895 783 0 )); + DATA(insert ( 3919 3831 3831 11 s 3896 783 0 )); + DATA(insert ( 3919 3831 3831 12 s 3897 783 0 )); + #endif /* PG_AMOP_H */ *** a/src/include/catalog/pg_amproc.h --- b/src/include/catalog/pg_amproc.h *************** *** 336,340 **** DATA(insert ( 3659 3614 3614 4 3658 )); --- 336,349 ---- DATA(insert ( 3659 3614 3614 5 2700 )); DATA(insert ( 3626 3614 3614 1 3622 )); DATA(insert ( 3683 3615 3615 1 3668 )); + DATA(insert ( 3901 3831 3831 1 3870 )); + DATA(insert ( 3903 3831 3831 1 3902 )); + DATA(insert ( 3919 3831 3831 1 3875 )); + DATA(insert ( 3919 3831 3831 2 3876 )); + DATA(insert ( 3919 3831 3831 3 3877 )); + DATA(insert ( 3919 3831 3831 4 3878 )); + DATA(insert ( 3919 3831 3831 5 3879 )); + DATA(insert ( 3919 3831 3831 6 3880 )); + DATA(insert ( 3919 3831 3831 7 3881 )); #endif /* PG_AMPROC_H */ *** a/src/include/catalog/pg_opclass.h --- b/src/include/catalog/pg_opclass.h *************** *** 213,217 **** DATA(insert ( 783 tsvector_ops PGNSP PGUID 3655 3614 t 3642 )); --- 213,220 ---- DATA(insert ( 2742 tsvector_ops PGNSP PGUID 3659 3614 t 25 )); DATA(insert ( 403 tsquery_ops PGNSP PGUID 3683 3615 t 0 )); DATA(insert ( 783 tsquery_ops PGNSP PGUID 3702 3615 t 20 )); + DATA(insert ( 403 range_ops PGNSP PGUID 3901 3831 t 0 )); + DATA(insert ( 405 range_ops PGNSP PGUID 3903 3831 t 0 )); + DATA(insert ( 783 range_ops PGNSP PGUID 3919 3831 t 0 )); #endif /* PG_OPCLASS_H */ *** a/src/include/catalog/pg_operator.h --- b/src/include/catalog/pg_operator.h *************** *** 1661,1666 **** DESCR("less than or equal"); --- 1661,1709 ---- DATA(insert OID = 2993 ( ">=" PGNSP PGUID b f f 2249 2249 16 2992 2990 record_ge scalargtsel scalargtjoinsel )); DESCR("greater than or equal"); + /* generic range type operators */ + DATA(insert OID = 3882 ( "=" PGNSP PGUID b t t 3831 3831 16 3882 3883 range_eq eqsel eqjoinsel )); + DESCR("equal"); + DATA(insert OID = 3883 ( "<>" PGNSP PGUID b f f 3831 3831 16 3883 3882 range_ne neqsel neqjoinsel )); + DESCR("not equal"); + DATA(insert OID = 3884 ( "<" PGNSP PGUID b f f 3831 3831 16 3887 3886 range_lt eqsel eqjoinsel )); + DESCR("less than"); + DATA(insert OID = 3885 ( "<=" PGNSP PGUID b f f 3831 3831 16 3886 3887 range_le eqsel eqjoinsel )); + DESCR("less than or equal"); + DATA(insert OID = 3886 ( ">=" PGNSP PGUID b f f 3831 3831 16 3885 3884 range_ge eqsel eqjoinsel )); + DESCR("greater than or equal"); + DATA(insert OID = 3887 ( ">" PGNSP PGUID b f f 3831 3831 16 3884 3885 range_gt eqsel eqjoinsel )); + DESCR("greater than"); + DATA(insert OID = 3888 ( "&&" PGNSP PGUID b f f 3831 3831 16 3888 0 3857 scalarltsel scalarltjoinsel )); + DESCR("overlaps"); + DATA(insert OID = 3889 ( "@>" PGNSP PGUID b f f 3831 2776 16 3891 0 3858 scalargtsel scalargtjoinsel )); + DESCR("contains"); + DATA(insert OID = 3890 ( "@>" PGNSP PGUID b f f 3831 3831 16 3892 0 3859 scalargtsel scalargtjoinsel )); + DESCR("contains"); + DATA(insert OID = 3891 ( "<@" PGNSP PGUID b f f 2776 3831 16 3889 0 3860 scalargtsel scalargtjoinsel )); + DESCR("contained by"); + DATA(insert OID = 3892 ( "<@" PGNSP PGUID b f f 3831 3831 16 3890 0 3861 scalargtsel scalargtjoinsel )); + DESCR("contained by"); + DATA(insert OID = 3893 ( "<<" PGNSP PGUID b f f 3831 3831 16 0 0 before scalargtsel scalargtjoinsel )); + DESCR("left of"); + DATA(insert OID = 3894 ( ">>" PGNSP PGUID b f f 3831 3831 16 0 0 after scalargtsel scalargtjoinsel )); + DESCR("right of"); + DATA(insert OID = 3895 ( "&<" PGNSP PGUID b f f 3831 3831 16 0 0 overleft scalarltsel scalarltjoinsel )); + DESCR("overlaps to left"); + DATA(insert OID = 3896 ( "&>" PGNSP PGUID b f f 3831 3831 16 0 0 overright scalargtsel scalargtjoinsel )); + DESCR("overlaps to right"); + DATA(insert OID = 3897 ( "-|-" PGNSP PGUID b f f 3831 3831 16 3897 0 adjacent scalargtsel scalargtjoinsel )); + DESCR("adjacent"); + DATA(insert OID = 3898 ( "+" PGNSP PGUID b f f 3831 3831 3831 3898 0 range_union - - )); + DESCR("range union"); + DATA(insert OID = 3899 ( "-" PGNSP PGUID b f f 3831 3831 3831 0 0 minus - - )); + DESCR("range difference"); + DATA(insert OID = 3900 ( "*" PGNSP PGUID b f f 3831 3831 3831 3900 0 range_intersect - - )); + DESCR("intersection"); + DATA(insert OID = 3920 ( "!?" PGNSP PGUID r f f 3831 0 16 0 3921 3850 - - )); + DESCR("empty"); + DATA(insert OID = 3921 ( "?" PGNSP PGUID r f f 3831 0 16 0 3920 3816 - - )); + DESCR("non-empty"); /* * function prototypes *** a/src/include/catalog/pg_opfamily.h --- b/src/include/catalog/pg_opfamily.h *************** *** 139,143 **** DATA(insert OID = 3655 ( 783 tsvector_ops PGNSP PGUID )); --- 139,146 ---- DATA(insert OID = 3659 ( 2742 tsvector_ops PGNSP PGUID )); DATA(insert OID = 3683 ( 403 tsquery_ops PGNSP PGUID )); DATA(insert OID = 3702 ( 783 tsquery_ops PGNSP PGUID )); + DATA(insert OID = 3901 ( 403 range_ops PGNSP PGUID )); + DATA(insert OID = 3903 ( 405 range_ops PGNSP PGUID )); + DATA(insert OID = 3919 ( 783 range_ops PGNSP PGUID )); #endif /* PG_OPFAMILY_H */ *** a/src/include/catalog/pg_proc.h --- b/src/include/catalog/pg_proc.h *************** *** 1216,1221 **** DATA(insert OID = 1180 ( abstime PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 702 --- 1216,1227 ---- DESCR("convert timestamp with time zone to abstime"); DATA(insert OID = 1181 ( age PGNSP PGUID 12 1 0 0 0 f f f t f s 1 0 23 "28" _null_ _null_ _null_ _null_ xid_age _null_ _null_ _null_ )); DESCR("age of a transaction ID, in transactions before current transaction"); + DATA(insert OID = 3916 ( float8 PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 701 "1114" _null_ _null_ _null_ _null_ timestamp_float8 _null_ _null_ _null_ )); + DESCR("convert timestamp to float8"); + DATA(insert OID = 3917 ( float8 PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 701 "1184" _null_ _null_ _null_ _null_ timestamp_float8 _null_ _null_ _null_ )); + DESCR("convert timestamp with time zone to float8"); + DATA(insert OID = 3918 ( float8 PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 701 "1082" _null_ _null_ _null_ _null_ date_float8 _null_ _null_ _null_ )); + DESCR("convert date to float8"); DATA(insert OID = 1188 ( timestamptz_mi PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 1186 "1184 1184" _null_ _null_ _null_ _null_ timestamp_mi _null_ _null_ _null_ )); DATA(insert OID = 1189 ( timestamptz_pl_interval PGNSP PGUID 12 1 0 0 0 f f f t f s 2 0 1184 "1184 1186" _null_ _null_ _null_ _null_ timestamptz_pl_interval _null_ _null_ _null_ )); *************** *** 4331,4336 **** DESCR("fetch the last row value"); --- 4337,4458 ---- DATA(insert OID = 3114 ( nth_value PGNSP PGUID 12 1 0 0 0 f t f t f i 2 0 2283 "2283 23" _null_ _null_ _null_ _null_ window_nth_value _null_ _null_ _null_ )); DESCR("fetch the Nth row value"); + /* procs for range types */ + DATA(insert OID = 3832 ( anyrange_in PGNSP PGUID 12 1 0 0 0 f f f t f s 3 0 3831 "2275 26 23" _null_ _null_ _null_ _null_ anyrange_in _null_ _null_ _null_ )); + DESCR("I/O"); + DATA(insert OID = 3833 ( anyrange_out PGNSP PGUID 12 1 0 0 0 f f f t f s 1 0 2275 "3831" _null_ _null_ _null_ _null_ anyrange_out _null_ _null_ _null_ )); + DESCR("I/O"); + DATA(insert OID = 3834 ( range_in PGNSP PGUID 12 1 0 0 0 f f f t f s 3 0 3831 "2275 26 23" _null_ _null_ _null_ _null_ range_in _null_ _null_ _null_ )); + DESCR("I/O"); + DATA(insert OID = 3835 ( range_out PGNSP PGUID 12 1 0 0 0 f f f t f s 1 0 2275 "3831" _null_ _null_ _null_ _null_ range_out _null_ _null_ _null_ )); + DESCR("I/O"); + DATA(insert OID = 3836 ( range_recv PGNSP PGUID 12 1 0 0 0 f f f t f s 3 0 3831 "2281 26 23" _null_ _null_ _null_ _null_ range_recv _null_ _null_ _null_ )); + DESCR("I/O"); + DATA(insert OID = 3837 ( range_send PGNSP PGUID 12 1 0 0 0 f f f t f s 1 0 17 "3831" _null_ _null_ _null_ _null_ range_send _null_ _null_ _null_ )); + DESCR("I/O"); + DATA(insert OID = 3838 ( range PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 3831 "2283" _null_ _null_ _null_ _null_ range_make1 _null_ _null_ _null_ )); + DESCR("construct a range from a single point"); + DATA(insert OID = 3839 ( range PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 3831 "2283 2283" _null_ _null_ _null_ _null_ range _null_ _null_ _null_ )); + DESCR("construct a left-inclusive range from two points"); + DATA(insert OID = 3840 ( range__ PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 3831 "2283 2283" _null_ _null_ _null_ _null_ range__ _null_ _null_ _null_ )); + DESCR("construct an exclusive range from two points"); + DATA(insert OID = 3841 ( range_i PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 3831 "2283 2283" _null_ _null_ _null_ _null_ range_i _null_ _null_ _null_ )); + DESCR("construct a right-inclusive range from two points"); + DATA(insert OID = 3842 ( rangei_ PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 3831 "2283 2283" _null_ _null_ _null_ _null_ rangei_ _null_ _null_ _null_ )); + DESCR("construct a left-inclusive range from two points"); + DATA(insert OID = 3843 ( rangeii PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 3831 "2283 2283" _null_ _null_ _null_ _null_ rangeii _null_ _null_ _null_ )); + DESCR("construct an inclusive range from two points"); + DATA(insert OID = 3844 ( range_linf_ PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 3831 "2283" _null_ _null_ _null_ _null_ range_linf_ _null_ _null_ _null_ )); + DESCR("construct a range with an infinite lower bound and exclusive upper bound"); + DATA(insert OID = 3845 ( range_uinf_ PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 3831 "2283" _null_ _null_ _null_ _null_ range_uinf_ _null_ _null_ _null_ )); + DESCR("construct a range with an infinite upper bound and exclusive lower bound"); + DATA(insert OID = 3846 ( range_linfi PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 3831 "2283" _null_ _null_ _null_ _null_ range_linfi _null_ _null_ _null_ )); + DESCR("construct a range with an infinite lower bound and inclusive upper bound"); + DATA(insert OID = 3847 ( range_uinfi PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 3831 "2283" _null_ _null_ _null_ _null_ range_uinfi _null_ _null_ _null_ )); + DESCR("construct a range with an infinite upper bound and inclusive lower bound"); + DATA(insert OID = 3848 ( lower PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 2283 "3831" _null_ _null_ _null_ _null_ range_lower _null_ _null_ _null_ )); + DESCR("return the range's lower bound"); + DATA(insert OID = 3849 ( upper PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 2283 "3831" _null_ _null_ _null_ _null_ range_upper _null_ _null_ _null_ )); + DESCR("return the range's upper bound"); + DATA(insert OID = 3850 ( empty PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 16 "3831" _null_ _null_ _null_ _null_ range_empty _null_ _null_ _null_ )); + DESCR("implementation of !? operator"); + DATA(insert OID = 3816 ( non_empty PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 16 "3831" _null_ _null_ _null_ _null_ range_non_empty _null_ _null_ _null_ )); + DESCR("implementation of ? operator"); + DATA(insert OID = 3851 ( lower_inc PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 16 "3831" _null_ _null_ _null_ _null_ range_lower_inc _null_ _null_ _null_ )); + DESCR("is the range's lower bound inclusive?"); + DATA(insert OID = 3852 ( upper_inc PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 16 "3831" _null_ _null_ _null_ _null_ range_upper_inc _null_ _null_ _null_ )); + DESCR("is the range's upper bound inclusive?"); + DATA(insert OID = 3853 ( lower_inf PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 16 "3831" _null_ _null_ _null_ _null_ range_lower_inf _null_ _null_ _null_ )); + DESCR("is the range's lower bound infinite?"); + DATA(insert OID = 3854 ( upper_inf PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 16 "3831" _null_ _null_ _null_ _null_ range_upper_inf _null_ _null_ _null_ )); + DESCR("is the range's upper bound infinite?"); + DATA(insert OID = 3855 ( range_eq PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_eq _null_ _null_ _null_ )); + DESCR("implementation of = operator"); + DATA(insert OID = 3856 ( range_ne PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_ne _null_ _null_ _null_ )); + DESCR("implementation of <> operator"); + DATA(insert OID = 3857 ( overlaps PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_overlaps _null_ _null_ _null_ )); + DESCR("implementation of && operator"); + DATA(insert OID = 3858 ( contains PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "3831 2776" _null_ _null_ _null_ _null_ range_contains_elem _null_ _null_ _null_ )); + DESCR("implementation of @> operator"); + DATA(insert OID = 3859 ( contains PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_contains _null_ _null_ _null_ )); + DESCR("implementation of @> operator"); + DATA(insert OID = 3860 ( contained_by PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "2776 3831" _null_ _null_ _null_ _null_ elem_contained_by_range _null_ _null_ _null_ )); + DESCR("implementation of <@ operator"); + DATA(insert OID = 3861 ( contained_by PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_contained_by _null_ _null_ _null_ )); + DESCR("implementation of <@ operator"); + DATA(insert OID = 3862 ( adjacent PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_adjacent _null_ _null_ _null_ )); + DESCR("implementation of -|- operator"); + DATA(insert OID = 3863 ( before PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_before _null_ _null_ _null_ )); + DESCR("implementation of << operator"); + DATA(insert OID = 3864 ( after PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_after _null_ _null_ _null_ )); + DESCR("implementation of >> operator"); + DATA(insert OID = 3865 ( overleft PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_overleft _null_ _null_ _null_ )); + DESCR("implementation of &< operator"); + DATA(insert OID = 3866 ( overright PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_overright _null_ _null_ _null_ )); + DESCR("implementation of &> operator"); + DATA(insert OID = 3867 ( range_union PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 3831 "3831 3831" _null_ _null_ _null_ _null_ range_union _null_ _null_ _null_ )); + DESCR("implementation of + operator"); + DATA(insert OID = 3868 ( range_intersect PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 3831 "3831 3831" _null_ _null_ _null_ _null_ range_intersect _null_ _null_ _null_ )); + DESCR("implementation of * operator"); + DATA(insert OID = 3869 ( minus PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 3831 "3831 3831" _null_ _null_ _null_ _null_ range_minus _null_ _null_ _null_ )); + DESCR("implementation of - operator"); + DATA(insert OID = 3870 ( range_cmp PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 23 "3831 3831" _null_ _null_ _null_ _null_ range_cmp _null_ _null_ _null_ )); + DESCR("less-equal-greater"); + DATA(insert OID = 3871 ( range_lt PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_lt _null_ _null_ _null_ )); + DATA(insert OID = 3872 ( range_le PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_le _null_ _null_ _null_ )); + DATA(insert OID = 3873 ( range_ge PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_ge _null_ _null_ _null_ )); + DATA(insert OID = 3874 ( range_gt PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_gt _null_ _null_ _null_ )); + DATA(insert OID = 3875 ( range_gist_consistent PGNSP PGUID 12 1 0 0 0 f f f t f i 5 0 16 "2281 3831 21 26 2281" _null_ _null_ _null_ _null_ range_gist_consistent _null_ _null_ _null_ )); + DESCR("the set difference of the two ranges"); + DATA(insert OID = 3876 ( range_gist_union PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ range_gist_union _null_ _null_ _null_ )); + DESCR("the set difference of the two ranges"); + DATA(insert OID = 3877 ( range_gist_compress PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 2281 "2281" _null_ _null_ _null_ _null_ range_gist_compress _null_ _null_ _null_ )); + DESCR("the set difference of the two ranges"); + DATA(insert OID = 3878 ( range_gist_decompress PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 2281 "2281" _null_ _null_ _null_ _null_ range_gist_decompress _null_ _null_ _null_ )); + DESCR("the set difference of the two ranges"); + DATA(insert OID = 3879 ( range_gist_penalty PGNSP PGUID 12 1 0 0 0 f f f t f i 3 0 2281 "2281 2281 2281" _null_ _null_ _null_ _null_ range_gist_penalty _null_ _null_ _null_ )); + DESCR("the set difference of the two ranges"); + DATA(insert OID = 3880 ( range_gist_picksplit PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ range_gist_picksplit _null_ _null_ _null_ )); + DESCR("the set difference of the two ranges"); + DATA(insert OID = 3881 ( range_gist_same PGNSP PGUID 12 1 0 0 0 f f f t f i 3 0 2281 "2281 2281 2281" _null_ _null_ _null_ _null_ range_gist_same _null_ _null_ _null_ )); + DESCR("the set difference of the two ranges"); + DATA(insert OID = 3902 ( hash_range PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 23 "3831" _null_ _null_ _null_ _null_ hash_range _null_ _null_ _null_ )); + DESCR("hash a range"); + DATA(insert OID = 3914 ( int4range_canonical PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 3904 "3904" _null_ _null_ _null_ _null_ int4range_canonical _null_ _null_ _null_ )); + DESCR("convert an int4 range to canonical form"); + DATA(insert OID = 3928 ( int8range_canonical PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 3926 "3926" _null_ _null_ _null_ _null_ int8range_canonical _null_ _null_ _null_ )); + DESCR("convert an int8 range to canonical form"); + DATA(insert OID = 3915 ( daterange_canonical PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 3906 "3906" _null_ _null_ _null_ _null_ daterange_canonical _null_ _null_ _null_ )); + DESCR("convert a date range to canonical form"); + DATA(insert OID = 3922 ( length PGNSP PGUID 14 100 0 0 0 f f f t f i 1 0 2283 "3831" _null_ _null_ _null_ _null_ "select pg_catalog.upper($1) - pg_catalog.lower($1);" _null_ _null_ _null_ )); + DESCR("range upper bound minus range lower bound"); + DATA(insert OID = 3923 ( length PGNSP PGUID 14 100 0 0 0 f f f t f i 1 0 1186 "3908" _null_ _null_ _null_ _null_ "select pg_catalog.upper($1) - pg_catalog.lower($1);" _null_ _null_ _null_ )); + DESCR("range upper bound minus range lower bound"); + DATA(insert OID = 3924 ( length PGNSP PGUID 14 100 0 0 0 f f f t f i 1 0 1186 "3910" _null_ _null_ _null_ _null_ "select pg_catalog.upper($1) - pg_catalog.lower($1);" _null_ _null_ _null_ )); + DESCR("range upper bound minus range lower bound"); + DATA(insert OID = 3925 ( length PGNSP PGUID 14 100 0 0 0 f f f t f i 1 0 23 "3912" _null_ _null_ _null_ _null_ "select pg_catalog.upper($1) - pg_catalog.lower($1);" _null_ _null_ _null_ )); + DESCR("range upper bound minus range lower bound"); + /* * Symbolic values for provolatile column: these indicate whether the result *** /dev/null --- b/src/include/catalog/pg_range.h *************** *** 0 **** --- 1,82 ---- + /*------------------------------------------------------------------------- + * + * pg_range.h + * definition of the system "range" relation (pg_range) + * along with the relation's initial contents. + * + * + * Copyright (c) 2006-2010, PostgreSQL Global Development Group + * + * src/include/catalog/pg_range.h + * + * NOTES + * the genbki.pl script reads this file and generates .bki + * information from the DATA() statements. + * + * XXX do NOT break up DATA() statements into multiple lines! + * the scripts are not as smart as you might think... + * + *------------------------------------------------------------------------- + */ + #ifndef PG_RANGE_H + #define PG_RANGE_H + + #include "catalog/genbki.h" + + /* ---------------- + * pg_range definition. cpp turns this into + * typedef struct FormData_pg_range + * ---------------- + */ + #define RangeRelationId 3541 + + CATALOG(pg_range,3541) BKI_WITHOUT_OIDS + { + Oid rngtypid; /* OID of owning range type */ + Oid rngsubtype; /* OID of range's subtype */ + Oid rngcollation; /* collation for this range type, or 0 */ + regproc rngsubopc; /* subtype's btree opclass */ + regproc rngcanonical; /* canonicalize range, or 0 */ + regproc rngsubfloat; /* represent subtype as a float8 (for GiST) */ + } FormData_pg_range; + + /* ---------------- + * Form_pg_range corresponds to a pointer to a tuple with + * the format of pg_range relation. + * ---------------- + */ + typedef FormData_pg_range *Form_pg_range; + + /* ---------------- + * compiler constants for pg_range + * ---------------- + */ + #define Natts_pg_range 6 + #define Anum_pg_range_rngtypid 1 + #define Anum_pg_range_rngsubtype 2 + #define Anum_pg_range_rngcollation 3 + #define Anum_pg_range_rngsubopc 4 + #define Anum_pg_range_rngcanonical 5 + #define Anum_pg_range_rngsubfloat 6 + + /* + * prototypes for functions in pg_range.c + */ + + void RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation, + Oid rangeSubOpclass, regproc rangeCanonical, + regproc rangeSubFloat); + void RangeDelete(Oid rangeTypeOid); + + /* ---------------- + * initial contents of pg_range + * ---------------- + */ + DATA(insert ( 3904 23 0 1978 int4range_canonical 316 )); + DATA(insert ( 3906 1700 0 10037 - 1746 )); + DATA(insert ( 3908 1114 0 10054 - 3916 )); + DATA(insert ( 3910 1184 0 10047 - 3917 )); + DATA(insert ( 3912 1082 0 10019 daterange_canonical 3918 )); + DATA(insert ( 3926 20 0 10029 int8range_canonical 482 )); + + #endif /* PG_RANGE_H */ *** a/src/include/catalog/pg_type.h --- b/src/include/catalog/pg_type.h *************** *** 591,596 **** DATA(insert OID = 2970 ( txid_snapshot PGNSP PGUID -1 f b U f t \054 0 0 2949 tx --- 591,623 ---- DESCR("txid snapshot"); DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 0 _null_ _null_ )); + /* range types */ + + DATA(insert OID = 3904 ( int4range PGNSP PGUID -1 f r R f t \054 0 0 3905 range_in range_out range_recv range_send - - - i x f 0 -1 0 0 _null_ _null_ )); + #define INT4RANGEOID 3904 + DATA(insert OID = 3905 ( _int4range PGNSP PGUID -1 f b A f t \054 0 3904 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ )); + #define INT4RANGEARRAYOID 3905 + DATA(insert OID = 3906 ( numrange PGNSP PGUID -1 f r R f t \054 0 0 3907 range_in range_out range_recv range_send - - - i x f 0 -1 0 0 _null_ _null_ )); + #define NUMRANGEOID 3906 + DATA(insert OID = 3907 ( _numrange PGNSP PGUID -1 f b A f t \054 0 3906 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ )); + #define NUMRANGEARRAYOID 3907 + DATA(insert OID = 3908 ( tsrange PGNSP PGUID -1 f r R f t \054 0 0 3909 range_in range_out range_recv range_send - - - i x f 0 -1 0 0 _null_ _null_ )); + #define TSRANGEOID 3908 + DATA(insert OID = 3909 ( _tsrange PGNSP PGUID -1 f b A f t \054 0 3908 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ )); + #define TSRANGEARRAYOID 3909 + DATA(insert OID = 3910 ( tstzrange PGNSP PGUID -1 f r R f t \054 0 0 3911 range_in range_out range_recv range_send - - - i x f 0 -1 0 0 _null_ _null_ )); + #define TSTZRANGEOID 3910 + DATA(insert OID = 3911 ( _tstzrange PGNSP PGUID -1 f b A f t \054 0 3910 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ )); + #define TSTZRANGEARRAYOID 3911 + DATA(insert OID = 3912 ( daterange PGNSP PGUID -1 f r R f t \054 0 0 3913 range_in range_out range_recv range_send - - - i x f 0 -1 0 0 _null_ _null_ )); + #define DATERANGEOID 3912 + DATA(insert OID = 3913 ( _daterange PGNSP PGUID -1 f b A f t \054 0 3912 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ )); + #define DATERANGEARRAYOID 3913 + DATA(insert OID = 3926 ( int8range PGNSP PGUID -1 f r R f t \054 0 0 3927 range_in range_out range_recv range_send - - - i x f 0 -1 0 0 _null_ _null_ )); + #define INT8RANGEOID 3926 + DATA(insert OID = 3927 ( _int8range PGNSP PGUID -1 f b A f t \054 0 3926 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ )); + #define INT8RANGEARRAYOID 3927 + /* * pseudo-types * *************** *** 632,637 **** DATA(insert OID = 3500 ( anyenum PGNSP PGUID 4 t p P f t \054 0 0 0 anyenum_in --- 659,666 ---- #define ANYENUMOID 3500 DATA(insert OID = 3115 ( fdw_handler PGNSP PGUID 4 t p P f t \054 0 0 0 fdw_handler_in fdw_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ )); #define FDW_HANDLEROID 3115 + DATA(insert OID = 3831 ( anyrange PGNSP PGUID 4 t p P f t \054 0 0 0 anyrange_in anyrange_out - - - - - i p f 0 -1 0 0 _null_ _null_ )); + #define ANYRANGEOID 3831 /* *************** *** 642,647 **** DATA(insert OID = 3115 ( fdw_handler PGNSP PGUID 4 t p P f t \054 0 0 0 fdw_han --- 671,677 ---- #define TYPTYPE_DOMAIN 'd' /* domain over another type */ #define TYPTYPE_ENUM 'e' /* enumerated type */ #define TYPTYPE_PSEUDO 'p' /* pseudo-type */ + #define TYPTYPE_RANGE 'r' /* range type */ #define TYPCATEGORY_INVALID '\0' /* not an allowed category */ #define TYPCATEGORY_ARRAY 'A' *************** *** 653,658 **** DATA(insert OID = 3115 ( fdw_handler PGNSP PGUID 4 t p P f t \054 0 0 0 fdw_han --- 683,689 ---- #define TYPCATEGORY_NETWORK 'I' /* think INET */ #define TYPCATEGORY_NUMERIC 'N' #define TYPCATEGORY_PSEUDOTYPE 'P' + #define TYPCATEGORY_RANGE 'R' #define TYPCATEGORY_STRING 'S' #define TYPCATEGORY_TIMESPAN 'T' #define TYPCATEGORY_USER 'U' *************** *** 664,669 **** DATA(insert OID = 3115 ( fdw_handler PGNSP PGUID 4 t p P f t \054 0 0 0 fdw_han ((typid) == ANYELEMENTOID || \ (typid) == ANYARRAYOID || \ (typid) == ANYNONARRAYOID || \ ! (typid) == ANYENUMOID) #endif /* PG_TYPE_H */ --- 695,701 ---- ((typid) == ANYELEMENTOID || \ (typid) == ANYARRAYOID || \ (typid) == ANYNONARRAYOID || \ ! (typid) == ANYENUMOID || \ ! (typid) == ANYRANGEOID) #endif /* PG_TYPE_H */ *** a/src/include/commands/typecmds.h --- b/src/include/commands/typecmds.h *************** *** 24,29 **** extern void RemoveTypes(DropStmt *drop); --- 24,30 ---- extern void RemoveTypeById(Oid typeOid); extern void DefineDomain(CreateDomainStmt *stmt); extern void DefineEnum(CreateEnumStmt *stmt); + extern void DefineRange(CreateRangeStmt *stmt); extern void AlterEnum(AlterEnumStmt *stmt); extern Oid DefineCompositeType(const RangeVar *typevar, List *coldeflist); extern Oid AssignTypeArrayOid(void); *** a/src/include/nodes/nodes.h --- b/src/include/nodes/nodes.h *************** *** 344,349 **** typedef enum NodeTag --- 344,350 ---- T_ReassignOwnedStmt, T_CompositeTypeStmt, T_CreateEnumStmt, + T_CreateRangeStmt, T_AlterEnumStmt, T_AlterTSDictionaryStmt, T_AlterTSConfigurationStmt, *** a/src/include/nodes/parsenodes.h --- b/src/include/nodes/parsenodes.h *************** *** 2332,2337 **** typedef struct AlterEnumStmt --- 2332,2348 ---- } AlterEnumStmt; /* ---------------------- + * Create Type Statement, enum types + * ---------------------- + */ + typedef struct CreateRangeStmt + { + NodeTag type; + List *typeName; /* qualified name (list of Value strings) */ + List *params; /* range parameters (list of DefElem) */ + } CreateRangeStmt; + + /* ---------------------- * Create View Statement * ---------------------- */ *** a/src/include/utils/date.h --- b/src/include/utils/date.h *************** *** 146,151 **** extern Datum date_timestamptz(PG_FUNCTION_ARGS); --- 146,152 ---- extern Datum timestamptz_date(PG_FUNCTION_ARGS); extern Datum datetime_timestamp(PG_FUNCTION_ARGS); extern Datum abstime_date(PG_FUNCTION_ARGS); + extern Datum date_float8(PG_FUNCTION_ARGS); extern Datum time_in(PG_FUNCTION_ARGS); extern Datum time_out(PG_FUNCTION_ARGS); *** a/src/include/utils/lsyscache.h --- b/src/include/utils/lsyscache.h *************** *** 101,106 **** extern Oid get_rel_namespace(Oid relid); --- 101,107 ---- extern Oid get_rel_type_id(Oid relid); extern char get_rel_relkind(Oid relid); extern Oid get_rel_tablespace(Oid relid); + extern char get_typalign(Oid typid); extern bool get_typisdefined(Oid typid); extern int16 get_typlen(Oid typid); extern bool get_typbyval(Oid typid); *************** *** 121,126 **** extern Node *get_typdefault(Oid typid); --- 122,128 ---- extern char get_typtype(Oid typid); extern bool type_is_rowtype(Oid typid); extern bool type_is_enum(Oid typid); + extern bool type_is_range(Oid typid); extern void get_type_category_preferred(Oid typid, char *typcategory, bool *typispreferred); *************** *** 149,154 **** extern void free_attstatsslot(Oid atttype, --- 151,162 ---- Datum *values, int nvalues, float4 *numbers, int nnumbers); extern char *get_namespace_name(Oid nspid); + extern Oid get_range_subtype(Oid rangeOid); + extern Oid get_range_collation(Oid rangeOid); + extern Oid get_range_from_subtype(Oid subtypeOid); + extern Oid get_range_sub_opclass(Oid rangeOid); + extern Oid get_range_subtype_float(Oid rangeOid); + extern Oid get_range_canonical(Oid rangeOid); #define type_is_array(typid) (get_element_type(typid) != InvalidOid) /* type_is_array_domain accepts both plain arrays and domains over arrays */ *** /dev/null --- b/src/include/utils/rangetypes.h *************** *** 0 **** --- 1,126 ---- + /*------------------------------------------------------------------------- + * + * rangetypes.h + * Declarations for Postgres range types. + * + */ + + #ifndef RANGETYPES_H + #define RANGETYPES_H + + #include "fmgr.h" + + typedef struct varlena RangeType; + + typedef struct + { + Datum val; + Oid rngtypid; + bool infinite; + bool lower; + bool inclusive; + } RangeBound; + + /* + * fmgr macros for range type objects + */ + #define DatumGetRangeType(X) ((RangeType *) PG_DETOAST_DATUM(X)) + #define DatumGetRangeTypeCopy(X) ((RangeType *) PG_DETOAST_DATUM_COPY(X)) + #define RangeTypeGetDatum(X) PointerGetDatum(X) + #define PG_GETARG_RANGE(n) DatumGetRangeType(PG_GETARG_DATUM(n)) + #define PG_GETARG_RANGE_COPY(n) DatumGetRangeTypeCopy(PG_GETARG_DATUM(n)) + #define PG_RETURN_RANGE(x) return RangeTypeGetDatum(x) + + /* + * prototypes for functions defined in rangetypes.c + */ + + /* IO */ + extern Datum anyrange_in(PG_FUNCTION_ARGS); + extern Datum anyrange_out(PG_FUNCTION_ARGS); + extern Datum range_in(PG_FUNCTION_ARGS); + extern Datum range_out(PG_FUNCTION_ARGS); + extern Datum range_recv(PG_FUNCTION_ARGS); + extern Datum range_send(PG_FUNCTION_ARGS); + + /* constructors */ + extern Datum range_make1(PG_FUNCTION_ARGS); + extern Datum range_linf_(PG_FUNCTION_ARGS); + extern Datum range_uinf_(PG_FUNCTION_ARGS); + extern Datum range_linfi(PG_FUNCTION_ARGS); + extern Datum range_uinfi(PG_FUNCTION_ARGS); + extern Datum range(PG_FUNCTION_ARGS); + extern Datum range__(PG_FUNCTION_ARGS); + extern Datum range_i(PG_FUNCTION_ARGS); + extern Datum rangei_(PG_FUNCTION_ARGS); + extern Datum rangeii(PG_FUNCTION_ARGS); + + /* range -> subtype */ + extern Datum range_lower(PG_FUNCTION_ARGS); + extern Datum range_upper(PG_FUNCTION_ARGS); + + /* range -> bool */ + extern Datum range_empty(PG_FUNCTION_ARGS); + extern Datum range_non_empty(PG_FUNCTION_ARGS); + extern Datum range_lower_inc(PG_FUNCTION_ARGS); + extern Datum range_upper_inc(PG_FUNCTION_ARGS); + extern Datum range_lower_inf(PG_FUNCTION_ARGS); + extern Datum range_upper_inf(PG_FUNCTION_ARGS); + + /* range, point -> bool */ + extern Datum range_contains_elem(PG_FUNCTION_ARGS); + extern Datum elem_contained_by_range(PG_FUNCTION_ARGS); + + /* range, range -> bool */ + extern Datum range_eq(PG_FUNCTION_ARGS); + extern Datum range_ne(PG_FUNCTION_ARGS); + extern Datum range_contains(PG_FUNCTION_ARGS); + extern Datum range_contained_by(PG_FUNCTION_ARGS); + extern Datum range_before(PG_FUNCTION_ARGS); + extern Datum range_after(PG_FUNCTION_ARGS); + extern Datum range_adjacent(PG_FUNCTION_ARGS); + extern Datum range_overlaps(PG_FUNCTION_ARGS); + extern Datum range_overleft(PG_FUNCTION_ARGS); + extern Datum range_overright(PG_FUNCTION_ARGS); + + /* range, range -> range */ + extern Datum range_minus(PG_FUNCTION_ARGS); + extern Datum range_union(PG_FUNCTION_ARGS); + extern Datum range_intersect(PG_FUNCTION_ARGS); + + /* BTree support */ + extern Datum range_cmp(PG_FUNCTION_ARGS); + extern Datum range_lt(PG_FUNCTION_ARGS); + extern Datum range_le(PG_FUNCTION_ARGS); + extern Datum range_ge(PG_FUNCTION_ARGS); + extern Datum range_gt(PG_FUNCTION_ARGS); + + /* Hash support */ + extern Datum hash_range(PG_FUNCTION_ARGS); + + /* GiST support (rangetypes_gist.c) */ + extern Datum range_gist_consistent(PG_FUNCTION_ARGS); + extern Datum range_gist_compress(PG_FUNCTION_ARGS); + extern Datum range_gist_decompress(PG_FUNCTION_ARGS); + extern Datum range_gist_union(PG_FUNCTION_ARGS); + extern Datum range_gist_penalty(PG_FUNCTION_ARGS); + extern Datum range_gist_picksplit(PG_FUNCTION_ARGS); + extern Datum range_gist_same(PG_FUNCTION_ARGS); + + /* Canonical functions */ + Datum int4range_canonical(PG_FUNCTION_ARGS); + Datum int8range_canonical(PG_FUNCTION_ARGS); + Datum daterange_canonical(PG_FUNCTION_ARGS); + + + /* for defining more generic functions */ + extern Datum make_range(RangeBound *lower, RangeBound *upper, bool empty); + extern void range_deserialize(RangeType *range, RangeBound *lower, + RangeBound *upper, bool *empty); + extern int range_cmp_bounds(RangeBound *b1, RangeBound *b2); + extern RangeType *make_empty_range(Oid rngtypid); + + /* for defining a range "canonicalize" function */ + extern Datum range_serialize(RangeBound *lower, RangeBound *upper, bool empty); + + #endif /* RANGETYPES_H */ *** a/src/include/utils/syscache.h --- b/src/include/utils/syscache.h *************** *** 70,75 **** enum SysCacheIdentifier --- 70,77 ---- OPFAMILYOID, PROCNAMEARGSNSP, PROCOID, + RANGETYPE, + RANGESUBTYPE, RELNAMENSP, RELOID, RULERELNAME, *** a/src/include/utils/timestamp.h --- b/src/include/utils/timestamp.h *************** *** 264,269 **** extern Datum interval_part(PG_FUNCTION_ARGS); --- 264,270 ---- extern Datum timestamp_zone(PG_FUNCTION_ARGS); extern Datum timestamp_izone(PG_FUNCTION_ARGS); extern Datum timestamp_timestamptz(PG_FUNCTION_ARGS); + extern Datum timestamp_float8(PG_FUNCTION_ARGS); extern Datum timestamptz_in(PG_FUNCTION_ARGS); extern Datum timestamptz_out(PG_FUNCTION_ARGS); *** a/src/pl/plpgsql/src/pl_comp.c --- b/src/pl/plpgsql/src/pl_comp.c *************** *** 494,499 **** do_compile(FunctionCallInfo fcinfo, --- 494,501 ---- { if (rettypeid == ANYARRAYOID) rettypeid = INT4ARRAYOID; + else if (rettypeid == ANYRANGEOID) + rettypeid = INT4RANGEOID; else /* ANYELEMENT or ANYNONARRAY */ rettypeid = INT4OID; /* XXX what could we use for ANYENUM? */ *************** *** 2123,2128 **** build_datatype(HeapTuple typeTup, int32 typmod, Oid collation) --- 2125,2131 ---- case TYPTYPE_BASE: case TYPTYPE_DOMAIN: case TYPTYPE_ENUM: + case TYPTYPE_RANGE: typ->ttype = PLPGSQL_TTYPE_SCALAR; break; case TYPTYPE_COMPOSITE: *************** *** 2377,2384 **** compute_function_hashkey(FunctionCallInfo fcinfo, /* * This is the same as the standard resolve_polymorphic_argtypes() function, * but with a special case for validation: assume that polymorphic arguments ! * are integer or integer-array. Also, we go ahead and report the error ! * if we can't resolve the types. */ static void plpgsql_resolve_polymorphic_argtypes(int numargs, --- 2380,2387 ---- /* * This is the same as the standard resolve_polymorphic_argtypes() function, * but with a special case for validation: assume that polymorphic arguments ! * are integer, integer-range or integer-array. Also, we go ahead and report ! * the error if we can't resolve the types. */ static void plpgsql_resolve_polymorphic_argtypes(int numargs, *************** *** 2411,2416 **** plpgsql_resolve_polymorphic_argtypes(int numargs, --- 2414,2422 ---- case ANYENUMOID: /* XXX dubious */ argtypes[i] = INT4OID; break; + case ANYRANGEOID: + argtypes[i] = INT4RANGEOID; + break; case ANYARRAYOID: argtypes[i] = INT4ARRAYOID; break; *** a/src/test/regress/expected/opr_sanity.out --- b/src/test/regress/expected/opr_sanity.out *************** *** 1015,1033 **** ORDER BY 1, 2, 3; --- 1015,1044 ---- 403 | 5 | ~>~ 405 | 1 | = 783 | 1 | << + 783 | 1 | = 783 | 1 | @@ 783 | 2 | &< + 783 | 2 | <> 783 | 3 | && 783 | 4 | &> + 783 | 4 | @> + 783 | 5 | <@ 783 | 5 | >> + 783 | 6 | @> 783 | 6 | ~= + 783 | 7 | <@ 783 | 7 | @> + 783 | 8 | << 783 | 8 | <@ 783 | 9 | &<| + 783 | 9 | >> + 783 | 10 | &< 783 | 10 | <<| 783 | 10 | <^ + 783 | 11 | &> 783 | 11 | >^ 783 | 11 | |>> + 783 | 12 | -|- 783 | 12 | |&> 783 | 13 | ~ 783 | 14 | @ *************** *** 1044,1050 **** ORDER BY 1, 2, 3; 2742 | 2 | @@@ 2742 | 3 | <@ 2742 | 4 | = ! (40 rows) -- Check that all opclass search operators have selectivity estimators. -- This is not absolutely required, but it seems a reasonable thing --- 1055,1061 ---- 2742 | 2 | @@@ 2742 | 3 | <@ 2742 | 4 | = ! (51 rows) -- Check that all opclass search operators have selectivity estimators. -- This is not absolutely required, but it seems a reasonable thing *** /dev/null --- b/src/test/regress/expected/rangetypes.out *************** *** 0 **** --- 1,746 ---- + CREATE TABLE numrange_test (nr NUMRANGE); + create index numrange_test_btree on numrange_test(nr); + SET enable_seqscan = f; + -- negative test; should fail + INSERT INTO numrange_test VALUES('-[1.1, 2.2)'); + ERROR: syntax error on range input "-[1.1, 2.2)", character 1 + LINE 1: INSERT INTO numrange_test VALUES('-[1.1, 2.2)'); + ^ + INSERT INTO numrange_test VALUES('[1.1, NULL)'); + ERROR: NULL range boundaries are not supported + LINE 1: INSERT INTO numrange_test VALUES('[1.1, NULL)'); + ^ + INSERT INTO numrange_test VALUES('[NULL, 2.2)'); + ERROR: NULL range boundaries are not supported + LINE 1: INSERT INTO numrange_test VALUES('[NULL, 2.2)'); + ^ + INSERT INTO numrange_test VALUES('[NULL, NULL)'); + ERROR: NULL range boundaries are not supported + LINE 1: INSERT INTO numrange_test VALUES('[NULL, NULL)'); + ^ + -- should succeed + INSERT INTO numrange_test VALUES('[-INF, INF)'); + INSERT INTO numrange_test VALUES('[3, INF]'); + INSERT INTO numrange_test VALUES('[-INF, 5)'); + INSERT INTO numrange_test VALUES(range(1.1, 2.2)); + INSERT INTO numrange_test VALUES('-'); + INSERT INTO numrange_test VALUES(range(1.7)); + SELECT empty(nr) FROM numrange_test; + empty + ------- + f + f + f + f + t + f + (6 rows) + + SELECT lower_inc(nr), lower(nr), upper(nr), upper_inc(nr) FROM numrange_test + WHERE NOT empty(nr) AND NOT lower_inf(nr) AND NOT upper_inf(nr); + lower_inc | lower | upper | upper_inc + -----------+-------+-------+----------- + t | 1.1 | 2.2 | f + t | 1.7 | 1.7 | t + (2 rows) + + SELECT * FROM numrange_test WHERE contains(nr, range(1.9,1.91)); + nr + --------------- + ( -INF, INF ) + ( -INF, 5 ) + [ 1.1, 2.2 ) + (3 rows) + + SELECT * FROM numrange_test WHERE nr @> range(1.0,10000.1); + nr + --------------- + ( -INF, INF ) + (1 row) + + SELECT * FROM numrange_test WHERE contained_by(range(-1e7,-10000.1), nr); + nr + --------------- + ( -INF, INF ) + ( -INF, 5 ) + (2 rows) + + SELECT * FROM numrange_test WHERE 1.9 <@ nr; + nr + --------------- + ( -INF, INF ) + ( -INF, 5 ) + [ 1.1, 2.2 ) + (3 rows) + + SELECT * FROM numrange_test WHERE nr = '-'; + nr + ---- + - + (1 row) + + SELECT * FROM numrange_test WHERE range_eq(nr, '(1.1, 2.2)'); + nr + ---- + (0 rows) + + SELECT * FROM numrange_test WHERE nr = '[1.1, 2.2)'; + nr + -------------- + [ 1.1, 2.2 ) + (1 row) + + select range(2.0, 1.0); + ERROR: range lower bound must be less than or equal to range upper bound + select range(2.0, 3.0) -|- range(3.0, 4.0); + ?column? + ---------- + t + (1 row) + + select adjacent(range(2.0, 3.0), range(3.1, 4.0)); + adjacent + ---------- + f + (1 row) + + select rangeii(2.0, 3.0) -|- range__(3.0, 4.0); + ?column? + ---------- + t + (1 row) + + select range(1.0, 2.0) -|- rangeii(2.0, 3.0); + ?column? + ---------- + t + (1 row) + + select adjacent(range_i(2.0, 3.0), range_i(1.0, 2.0)); + adjacent + ---------- + t + (1 row) + + select range(1.1, 3.3) <@ range(0.1,10.1); + ?column? + ---------- + t + (1 row) + + select range(0.1, 10.1) <@ range(1.1,3.3); + ?column? + ---------- + f + (1 row) + + select range(1.1, 2.2) - range(2.0, 3.0); + ?column? + -------------- + [ 1.1, 2.0 ) + (1 row) + + select range(1.1, 2.2) - range(2.2, 3.0); + ?column? + -------------- + [ 1.1, 2.2 ) + (1 row) + + select rangeii(1.1, 2.2) - range(2.0, 3.0); + ?column? + -------------- + [ 1.1, 2.0 ) + (1 row) + + select minus(rangeii(10.1, 12.2), range_i(110.0,120.2)); + minus + ---------------- + [ 10.1, 12.2 ] + (1 row) + + select minus(rangeii(10.1, 12.2), range_i(0.0,120.2)); + minus + ------- + - + (1 row) + + select rangeii(4.5, 5.5) && range(5.5, 6.5); + ?column? + ---------- + t + (1 row) + + select range(1.0, 2.0) << range(3.0, 4.0); + ?column? + ---------- + t + (1 row) + + select range(1.0, 2.0) >> range(3.0, 4.0); + ?column? + ---------- + f + (1 row) + + select range(3.0, 70.0) &< range(6.6, 100.0); + ?column? + ---------- + t + (1 row) + + select range(1.1, 2.2) < range(1.0, 200.2); + ?column? + ---------- + f + (1 row) + + select range(1.1, 2.2) < range__(1.1, 1.2); + ?column? + ---------- + t + (1 row) + + select range(1.0, 2.0) + range(2.0, 3.0); + ?column? + -------------- + [ 1.0, 3.0 ) + (1 row) + + select range(1.0, 2.0) + range(1.5, 3.0); + ?column? + -------------- + [ 1.0, 3.0 ) + (1 row) + + select range(1.0, 2.0) + range(2.5, 3.0); + ERROR: range union resulted in two ranges + select range(1.0, 2.0) * range(2.0, 3.0); + ?column? + ---------- + - + (1 row) + + select range(1.0, 2.0) * range(1.5, 3.0); + ?column? + -------------- + [ 1.5, 2.0 ) + (1 row) + + select range(1.0, 2.0) * range(2.5, 3.0); + ?column? + ---------- + - + (1 row) + + select * from numrange_test where nr < rangeii(-1000.0, -1000.0); + nr + --------------- + ( -INF, INF ) + ( -INF, 5 ) + - + (3 rows) + + select * from numrange_test where nr < rangeii(0.0, 1.0); + nr + --------------- + ( -INF, INF ) + ( -INF, 5 ) + - + (3 rows) + + select * from numrange_test where nr < rangeii(1000.0, 1001.0); + nr + --------------- + ( -INF, INF ) + [ 3, INF ) + ( -INF, 5 ) + [ 1.1, 2.2 ) + - + [ 1.7, 1.7 ] + (6 rows) + + select * from numrange_test where nr > rangeii(-1001.0, -1000.0); + nr + -------------- + [ 3, INF ) + [ 1.1, 2.2 ) + [ 1.7, 1.7 ] + (3 rows) + + select * from numrange_test where nr > rangeii(0.0, 1.0); + nr + -------------- + [ 3, INF ) + [ 1.1, 2.2 ) + [ 1.7, 1.7 ] + (3 rows) + + select * from numrange_test where nr > rangeii(1000.0, 1000.0); + nr + ---- + (0 rows) + + create table numrange_test2(nr numrange); + create index numrange_test2_hash_idx on numrange_test2 (nr); + INSERT INTO numrange_test2 VALUES('[-INF, 5)'); + INSERT INTO numrange_test2 VALUES(range(1.1, 2.2)); + INSERT INTO numrange_test2 VALUES(range(1.1, 2.2)); + INSERT INTO numrange_test2 VALUES(range__(1.1, 2.2)); + INSERT INTO numrange_test2 VALUES('-'); + select * from numrange_test2 where nr = '-'::numrange; + nr + ---- + - + (1 row) + + select * from numrange_test2 where nr = range(1.1, 2.2); + nr + -------------- + [ 1.1, 2.2 ) + [ 1.1, 2.2 ) + (2 rows) + + select * from numrange_test2 where nr = range(1.1, 2.3); + nr + ---- + (0 rows) + + set enable_nestloop=t; + set enable_hashjoin=f; + set enable_mergejoin=f; + select * from numrange_test natural join numrange_test2 order by nr; + nr + -------------- + - + ( -INF, 5 ) + [ 1.1, 2.2 ) + [ 1.1, 2.2 ) + (4 rows) + + set enable_nestloop=f; + set enable_hashjoin=t; + set enable_mergejoin=f; + select * from numrange_test natural join numrange_test2 order by nr; + nr + -------------- + - + ( -INF, 5 ) + [ 1.1, 2.2 ) + [ 1.1, 2.2 ) + (4 rows) + + set enable_nestloop=f; + set enable_hashjoin=f; + set enable_mergejoin=t; + select * from numrange_test natural join numrange_test2 order by nr; + nr + -------------- + - + ( -INF, 5 ) + [ 1.1, 2.2 ) + [ 1.1, 2.2 ) + (4 rows) + + set enable_nestloop to default; + set enable_hashjoin to default; + set enable_mergejoin to default; + SET enable_seqscan TO DEFAULT; + DROP TABLE numrange_test; + DROP TABLE numrange_test2; + -- test canonical form for int4range + select range__(1,10); + range__ + ----------- + [ 2, 10 ) + (1 row) + + select rangei_(1,10); + rangei_ + ----------- + [ 1, 10 ) + (1 row) + + select range_i(1,10); + range_i + ----------- + [ 2, 11 ) + (1 row) + + select rangeii(1,10); + rangeii + ----------- + [ 1, 11 ) + (1 row) + + -- test canonical form for daterange + select range__('2000-01-10'::date, '2000-01-20'::date); + range__ + ---------------------------- + [ 01-11-2000, 01-20-2000 ) + (1 row) + + select rangei_('2000-01-10'::date, '2000-01-20'::date); + rangei_ + ---------------------------- + [ 01-10-2000, 01-20-2000 ) + (1 row) + + select range_i('2000-01-10'::date, '2000-01-20'::date); + range_i + ---------------------------- + [ 01-11-2000, 01-21-2000 ) + (1 row) + + select rangeii('2000-01-10'::date, '2000-01-20'::date); + rangeii + ---------------------------- + [ 01-10-2000, 01-21-2000 ) + (1 row) + + -- test length() + select length(range(10.1,100.1)); + length + -------- + 90.0 + (1 row) + + select length('[2000-01-01 01:00:00, 2000-01-05 03:00:00)'::tsrange); + length + ------------------ + @ 4 days 2 hours + (1 row) + + select length('[2000-01-01 01:00:00, 2000-01-01 03:00:00)'::tstzrange); + length + ----------- + @ 2 hours + (1 row) + + select length('[2000-01-01, 2000-01-05)'::daterange); + length + -------- + 4 + (1 row) + + create table test_range_gist(ir int4range); + create index test_range_gist_idx on test_range_gist using gist (ir); + insert into test_range_gist select range(g, g+10) from generate_series(1,2000) g; + insert into test_range_gist select '-'::int4range from generate_series(1,500) g; + insert into test_range_gist select range(g, g+10000) from generate_series(1,1000) g; + insert into test_range_gist select '-'::int4range from generate_series(1,500) g; + insert into test_range_gist select range_linfi(g*10) from generate_series(1,100) g; + insert into test_range_gist select range_uinf_(g*10) from generate_series(1,100) g; + insert into test_range_gist select range(g, g+10) from generate_series(1,2000) g; + BEGIN; + SET LOCAL enable_seqscan = t; + SET LOCAL enable_bitmapscan = f; + SET LOCAL enable_indexscan = f; + select count(*) from test_range_gist where ir @> '-'::int4range; + count + ------- + 6200 + (1 row) + + select count(*) from test_range_gist where ir = range(10,20); + count + ------- + 2 + (1 row) + + select count(*) from test_range_gist where ir @> 10; + count + ------- + 130 + (1 row) + + select count(*) from test_range_gist where ir @> range(10,20); + count + ------- + 111 + (1 row) + + select count(*) from test_range_gist where ir && range(10,20); + count + ------- + 158 + (1 row) + + select count(*) from test_range_gist where ir <@ range(10,50); + count + ------- + 1062 + (1 row) + + select count(*) from (select * from test_range_gist where ir?) s where ir << range(100,500); + count + ------- + 189 + (1 row) + + select count(*) from (select * from test_range_gist where ir?) s where ir >> range(100,500); + count + ------- + 3554 + (1 row) + + select count(*) from (select * from test_range_gist where ir?) s where ir &< range(100,500); + count + ------- + 1029 + (1 row) + + select count(*) from (select * from test_range_gist where ir?) s where ir &> range(100,500); + count + ------- + 4794 + (1 row) + + select count(*) from (select * from test_range_gist where ir?) s where ir -|- range(100,500); + count + ------- + 5 + (1 row) + + COMMIT; + BEGIN; + SET LOCAL enable_seqscan = f; + SET LOCAL enable_bitmapscan = f; + SET LOCAL enable_indexscan = t; + select count(*) from test_range_gist where ir @> '-'::int4range; + count + ------- + 6200 + (1 row) + + select count(*) from test_range_gist where ir = range(10,20); + count + ------- + 2 + (1 row) + + select count(*) from test_range_gist where ir @> 10; + count + ------- + 130 + (1 row) + + select count(*) from test_range_gist where ir @> range(10,20); + count + ------- + 111 + (1 row) + + select count(*) from test_range_gist where ir && range(10,20); + count + ------- + 158 + (1 row) + + select count(*) from test_range_gist where ir <@ range(10,50); + count + ------- + 1062 + (1 row) + + select count(*) from test_range_gist where ir << range(100,500); + count + ------- + 189 + (1 row) + + select count(*) from test_range_gist where ir >> range(100,500); + count + ------- + 3554 + (1 row) + + select count(*) from test_range_gist where ir &< range(100,500); + count + ------- + 1029 + (1 row) + + select count(*) from test_range_gist where ir &> range(100,500); + count + ------- + 4794 + (1 row) + + select count(*) from test_range_gist where ir -|- range(100,500); + count + ------- + 5 + (1 row) + + COMMIT; + drop index test_range_gist_idx; + create index test_range_gist_idx on test_range_gist using gist (ir); + BEGIN; + SET LOCAL enable_seqscan = f; + SET LOCAL enable_bitmapscan = f; + SET LOCAL enable_indexscan = t; + select count(*) from test_range_gist where ir @> '-'::int4range; + count + ------- + 6200 + (1 row) + + select count(*) from test_range_gist where ir = range(10,20); + count + ------- + 2 + (1 row) + + select count(*) from test_range_gist where ir @> 10; + count + ------- + 130 + (1 row) + + select count(*) from test_range_gist where ir @> range(10,20); + count + ------- + 111 + (1 row) + + select count(*) from test_range_gist where ir && range(10,20); + count + ------- + 158 + (1 row) + + select count(*) from test_range_gist where ir <@ range(10,50); + count + ------- + 1062 + (1 row) + + select count(*) from test_range_gist where ir << range(100,500); + count + ------- + 189 + (1 row) + + select count(*) from test_range_gist where ir >> range(100,500); + count + ------- + 3554 + (1 row) + + select count(*) from test_range_gist where ir &< range(100,500); + count + ------- + 1029 + (1 row) + + select count(*) from test_range_gist where ir &> range(100,500); + count + ------- + 4794 + (1 row) + + select count(*) from test_range_gist where ir -|- range(100,500); + count + ------- + 5 + (1 row) + + COMMIT; + drop table test_range_gist; + -- + -- Btree_gist is not included by default, so to test exclusion + -- constraints with range types, use singleton int ranges for the "=" + -- portion of the constraint. + -- + create table test_range_excl( + room int4range, + speaker int4range, + during tsrange, + exclude using gist (room with =, during with &&), + exclude using gist (speaker with =, during with &&) + ); + NOTICE: CREATE TABLE / EXCLUDE will create implicit index "test_range_excl_room_during_excl" for table "test_range_excl" + NOTICE: CREATE TABLE / EXCLUDE will create implicit index "test_range_excl_speaker_during_excl" for table "test_range_excl" + insert into test_range_excl + values(range(123), range(1), '[2010-01-02 10:00, 2010-01-02 11:00)'); + insert into test_range_excl + values(range(123), range(2), '[2010-01-02 11:00, 2010-01-02 12:00)'); + insert into test_range_excl + values(range(123), range(3), '[2010-01-02 10:10, 2010-01-02 11:10)'); + ERROR: conflicting key value violates exclusion constraint "test_range_excl_room_during_excl" + DETAIL: Key (room, during)=([ 123, 124 ), [ Sat Jan 02 10:10:00 2010, Sat Jan 02 11:10:00 2010 )) conflicts with existing key (room, during)=([ 123, 124 ), [ Sat Jan 02 10:00:00 2010, Sat Jan 02 11:00:00 2010 )). + insert into test_range_excl + values(range(124), range(3), '[2010-01-02 10:10, 2010-01-02 11:10)'); + insert into test_range_excl + values(range(125), range(1), '[2010-01-02 10:10, 2010-01-02 11:10)'); + ERROR: conflicting key value violates exclusion constraint "test_range_excl_speaker_during_excl" + DETAIL: Key (speaker, during)=([ 1, 2 ), [ Sat Jan 02 10:10:00 2010, Sat Jan 02 11:10:00 2010 )) conflicts with existing key (speaker, during)=([ 1, 2 ), [ Sat Jan 02 10:00:00 2010, Sat Jan 02 11:00:00 2010 )). + drop table test_range_excl; + -- test bigint ranges + select range_i(10000000000::int8, 20000000000::int8); + range_i + ------------------------------ + [ 10000000001, 20000000001 ) + (1 row) + + -- test tstz ranges + set timezone to '-08'; + select '[2010-01-01 01:00:00 -05, 2010-01-01 02:00:00 -08)'::tstzrange; + tstzrange + ---------------------------------------------------------------- + [ Thu Dec 31 22:00:00 2009 -08, Fri Jan 01 02:00:00 2010 -08 ) + (1 row) + + -- should fail + select '[2010-01-01 01:00:00 -08, 2010-01-01 02:00:00 -05)'::tstzrange; + ERROR: range lower bound must be less than or equal to range upper bound + LINE 1: select '[2010-01-01 01:00:00 -08, 2010-01-01 02:00:00 -05)':... + ^ + set timezone to default; + -- + -- Test user-defined range of floats + -- + create type float8range as range (subtype=float8); + select '[123.001, 5.e9)'::float8range @> 888.882::float8; + ?column? + ---------- + t + (1 row) + + create table float8range_test(f8r float8range, i int); + insert into float8range_test values(range((-100.00007)::float8, '1.111113e9'::float8)); + select * from float8range_test; + f8r | i + ----------------------------+--- + [ -100.00007, 1111113000 ) | + (1 row) + + drop table float8range_test; + -- + -- Test rangetypes over domains + -- + create domain mydomain as int4; + create type mydomainrange as range(subtype=mydomain); + select '[4,50)'::mydomainrange @> 7::mydomain; + ?column? + ---------- + t + (1 row) + + -- + -- Test collations + -- + create type textrange_c as range(subtype=text, collation="C"); + select '[a, Z)'::textrange_c @> 'b'::text; + ERROR: range lower bound must be less than or equal to range upper bound + LINE 1: select '[a, Z)'::textrange_c @> 'b'::text; + ^ + drop type textrange_c; + create type textrange_en_us as range(subtype=text, collation="en_US"); + select '[a, Z)'::textrange_en_us @> 'b'::text; + ?column? + ---------- + t + (1 row) + + drop type textrange_en_us; *** a/src/test/regress/expected/sanity_check.out --- b/src/test/regress/expected/sanity_check.out *************** *** 116,121 **** SELECT relname, relhasindex --- 116,122 ---- pg_opfamily | t pg_pltemplate | t pg_proc | t + pg_range | t pg_rewrite | t pg_seclabel | t pg_shdepend | t *************** *** 158,164 **** SELECT relname, relhasindex timetz_tbl | f tinterval_tbl | f varchar_tbl | f ! (147 rows) -- -- another sanity check: every system catalog that has OIDs should have --- 159,165 ---- timetz_tbl | f tinterval_tbl | f varchar_tbl | f ! (148 rows) -- -- another sanity check: every system catalog that has OIDs should have *** a/src/test/regress/expected/type_sanity.out --- b/src/test/regress/expected/type_sanity.out *************** *** 17,23 **** SELECT p1.oid, p1.typname FROM pg_type as p1 WHERE p1.typnamespace = 0 OR (p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR ! (p1.typtype not in ('b', 'c', 'd', 'e', 'p')) OR NOT p1.typisdefined OR (p1.typalign not in ('c', 's', 'i', 'd')) OR (p1.typstorage not in ('p', 'x', 'e', 'm')); --- 17,23 ---- FROM pg_type as p1 WHERE p1.typnamespace = 0 OR (p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR ! (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r')) OR NOT p1.typisdefined OR (p1.typalign not in ('c', 's', 'i', 'd')) OR (p1.typstorage not in ('p', 'x', 'e', 'm')); *** a/src/test/regress/parallel_schedule --- b/src/test/regress/parallel_schedule *************** *** 13,19 **** test: tablespace # ---------- # The first group of parallel tests # ---------- ! test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeric txid uuid enum money # Depends on things setup during char, varchar and text test: strings --- 13,19 ---- # ---------- # The first group of parallel tests # ---------- ! test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeric txid uuid enum money rangetypes # Depends on things setup during char, varchar and text test: strings *** /dev/null --- b/src/test/regress/sql/rangetypes.sql *************** *** 0 **** --- 1,257 ---- + + CREATE TABLE numrange_test (nr NUMRANGE); + create index numrange_test_btree on numrange_test(nr); + SET enable_seqscan = f; + + -- negative test; should fail + INSERT INTO numrange_test VALUES('-[1.1, 2.2)'); + INSERT INTO numrange_test VALUES('[1.1, NULL)'); + INSERT INTO numrange_test VALUES('[NULL, 2.2)'); + INSERT INTO numrange_test VALUES('[NULL, NULL)'); + + -- should succeed + INSERT INTO numrange_test VALUES('[-INF, INF)'); + INSERT INTO numrange_test VALUES('[3, INF]'); + INSERT INTO numrange_test VALUES('[-INF, 5)'); + INSERT INTO numrange_test VALUES(range(1.1, 2.2)); + INSERT INTO numrange_test VALUES('-'); + INSERT INTO numrange_test VALUES(range(1.7)); + + SELECT empty(nr) FROM numrange_test; + SELECT lower_inc(nr), lower(nr), upper(nr), upper_inc(nr) FROM numrange_test + WHERE NOT empty(nr) AND NOT lower_inf(nr) AND NOT upper_inf(nr); + + SELECT * FROM numrange_test WHERE contains(nr, range(1.9,1.91)); + SELECT * FROM numrange_test WHERE nr @> range(1.0,10000.1); + SELECT * FROM numrange_test WHERE contained_by(range(-1e7,-10000.1), nr); + SELECT * FROM numrange_test WHERE 1.9 <@ nr; + SELECT * FROM numrange_test WHERE nr = '-'; + SELECT * FROM numrange_test WHERE range_eq(nr, '(1.1, 2.2)'); + SELECT * FROM numrange_test WHERE nr = '[1.1, 2.2)'; + + select range(2.0, 1.0); + + select range(2.0, 3.0) -|- range(3.0, 4.0); + select adjacent(range(2.0, 3.0), range(3.1, 4.0)); + select rangeii(2.0, 3.0) -|- range__(3.0, 4.0); + select range(1.0, 2.0) -|- rangeii(2.0, 3.0); + select adjacent(range_i(2.0, 3.0), range_i(1.0, 2.0)); + + select range(1.1, 3.3) <@ range(0.1,10.1); + select range(0.1, 10.1) <@ range(1.1,3.3); + + select range(1.1, 2.2) - range(2.0, 3.0); + select range(1.1, 2.2) - range(2.2, 3.0); + select rangeii(1.1, 2.2) - range(2.0, 3.0); + select minus(rangeii(10.1, 12.2), range_i(110.0,120.2)); + select minus(rangeii(10.1, 12.2), range_i(0.0,120.2)); + + select rangeii(4.5, 5.5) && range(5.5, 6.5); + select range(1.0, 2.0) << range(3.0, 4.0); + select range(1.0, 2.0) >> range(3.0, 4.0); + select range(3.0, 70.0) &< range(6.6, 100.0); + + select range(1.1, 2.2) < range(1.0, 200.2); + select range(1.1, 2.2) < range__(1.1, 1.2); + + select range(1.0, 2.0) + range(2.0, 3.0); + select range(1.0, 2.0) + range(1.5, 3.0); + select range(1.0, 2.0) + range(2.5, 3.0); + + select range(1.0, 2.0) * range(2.0, 3.0); + select range(1.0, 2.0) * range(1.5, 3.0); + select range(1.0, 2.0) * range(2.5, 3.0); + + select * from numrange_test where nr < rangeii(-1000.0, -1000.0); + select * from numrange_test where nr < rangeii(0.0, 1.0); + select * from numrange_test where nr < rangeii(1000.0, 1001.0); + select * from numrange_test where nr > rangeii(-1001.0, -1000.0); + select * from numrange_test where nr > rangeii(0.0, 1.0); + select * from numrange_test where nr > rangeii(1000.0, 1000.0); + + create table numrange_test2(nr numrange); + create index numrange_test2_hash_idx on numrange_test2 (nr); + INSERT INTO numrange_test2 VALUES('[-INF, 5)'); + INSERT INTO numrange_test2 VALUES(range(1.1, 2.2)); + INSERT INTO numrange_test2 VALUES(range(1.1, 2.2)); + INSERT INTO numrange_test2 VALUES(range__(1.1, 2.2)); + INSERT INTO numrange_test2 VALUES('-'); + + select * from numrange_test2 where nr = '-'::numrange; + select * from numrange_test2 where nr = range(1.1, 2.2); + select * from numrange_test2 where nr = range(1.1, 2.3); + + set enable_nestloop=t; + set enable_hashjoin=f; + set enable_mergejoin=f; + select * from numrange_test natural join numrange_test2 order by nr; + set enable_nestloop=f; + set enable_hashjoin=t; + set enable_mergejoin=f; + select * from numrange_test natural join numrange_test2 order by nr; + set enable_nestloop=f; + set enable_hashjoin=f; + set enable_mergejoin=t; + select * from numrange_test natural join numrange_test2 order by nr; + + set enable_nestloop to default; + set enable_hashjoin to default; + set enable_mergejoin to default; + SET enable_seqscan TO DEFAULT; + DROP TABLE numrange_test; + DROP TABLE numrange_test2; + + -- test canonical form for int4range + select range__(1,10); + select rangei_(1,10); + select range_i(1,10); + select rangeii(1,10); + + -- test canonical form for daterange + select range__('2000-01-10'::date, '2000-01-20'::date); + select rangei_('2000-01-10'::date, '2000-01-20'::date); + select range_i('2000-01-10'::date, '2000-01-20'::date); + select rangeii('2000-01-10'::date, '2000-01-20'::date); + + -- test length() + select length(range(10.1,100.1)); + select length('[2000-01-01 01:00:00, 2000-01-05 03:00:00)'::tsrange); + select length('[2000-01-01 01:00:00, 2000-01-01 03:00:00)'::tstzrange); + select length('[2000-01-01, 2000-01-05)'::daterange); + + create table test_range_gist(ir int4range); + create index test_range_gist_idx on test_range_gist using gist (ir); + + insert into test_range_gist select range(g, g+10) from generate_series(1,2000) g; + insert into test_range_gist select '-'::int4range from generate_series(1,500) g; + insert into test_range_gist select range(g, g+10000) from generate_series(1,1000) g; + insert into test_range_gist select '-'::int4range from generate_series(1,500) g; + insert into test_range_gist select range_linfi(g*10) from generate_series(1,100) g; + insert into test_range_gist select range_uinf_(g*10) from generate_series(1,100) g; + insert into test_range_gist select range(g, g+10) from generate_series(1,2000) g; + + BEGIN; + SET LOCAL enable_seqscan = t; + SET LOCAL enable_bitmapscan = f; + SET LOCAL enable_indexscan = f; + + select count(*) from test_range_gist where ir @> '-'::int4range; + select count(*) from test_range_gist where ir = range(10,20); + select count(*) from test_range_gist where ir @> 10; + select count(*) from test_range_gist where ir @> range(10,20); + select count(*) from test_range_gist where ir && range(10,20); + select count(*) from test_range_gist where ir <@ range(10,50); + select count(*) from (select * from test_range_gist where ir?) s where ir << range(100,500); + select count(*) from (select * from test_range_gist where ir?) s where ir >> range(100,500); + select count(*) from (select * from test_range_gist where ir?) s where ir &< range(100,500); + select count(*) from (select * from test_range_gist where ir?) s where ir &> range(100,500); + select count(*) from (select * from test_range_gist where ir?) s where ir -|- range(100,500); + COMMIT; + + BEGIN; + SET LOCAL enable_seqscan = f; + SET LOCAL enable_bitmapscan = f; + SET LOCAL enable_indexscan = t; + + select count(*) from test_range_gist where ir @> '-'::int4range; + select count(*) from test_range_gist where ir = range(10,20); + select count(*) from test_range_gist where ir @> 10; + select count(*) from test_range_gist where ir @> range(10,20); + select count(*) from test_range_gist where ir && range(10,20); + select count(*) from test_range_gist where ir <@ range(10,50); + select count(*) from test_range_gist where ir << range(100,500); + select count(*) from test_range_gist where ir >> range(100,500); + select count(*) from test_range_gist where ir &< range(100,500); + select count(*) from test_range_gist where ir &> range(100,500); + select count(*) from test_range_gist where ir -|- range(100,500); + COMMIT; + + drop index test_range_gist_idx; + create index test_range_gist_idx on test_range_gist using gist (ir); + + BEGIN; + SET LOCAL enable_seqscan = f; + SET LOCAL enable_bitmapscan = f; + SET LOCAL enable_indexscan = t; + + select count(*) from test_range_gist where ir @> '-'::int4range; + select count(*) from test_range_gist where ir = range(10,20); + select count(*) from test_range_gist where ir @> 10; + select count(*) from test_range_gist where ir @> range(10,20); + select count(*) from test_range_gist where ir && range(10,20); + select count(*) from test_range_gist where ir <@ range(10,50); + select count(*) from test_range_gist where ir << range(100,500); + select count(*) from test_range_gist where ir >> range(100,500); + select count(*) from test_range_gist where ir &< range(100,500); + select count(*) from test_range_gist where ir &> range(100,500); + select count(*) from test_range_gist where ir -|- range(100,500); + COMMIT; + + drop table test_range_gist; + + -- + -- Btree_gist is not included by default, so to test exclusion + -- constraints with range types, use singleton int ranges for the "=" + -- portion of the constraint. + -- + + create table test_range_excl( + room int4range, + speaker int4range, + during tsrange, + exclude using gist (room with =, during with &&), + exclude using gist (speaker with =, during with &&) + ); + + insert into test_range_excl + values(range(123), range(1), '[2010-01-02 10:00, 2010-01-02 11:00)'); + insert into test_range_excl + values(range(123), range(2), '[2010-01-02 11:00, 2010-01-02 12:00)'); + insert into test_range_excl + values(range(123), range(3), '[2010-01-02 10:10, 2010-01-02 11:10)'); + insert into test_range_excl + values(range(124), range(3), '[2010-01-02 10:10, 2010-01-02 11:10)'); + insert into test_range_excl + values(range(125), range(1), '[2010-01-02 10:10, 2010-01-02 11:10)'); + + drop table test_range_excl; + + -- test bigint ranges + select range_i(10000000000::int8, 20000000000::int8); + -- test tstz ranges + set timezone to '-08'; + select '[2010-01-01 01:00:00 -05, 2010-01-01 02:00:00 -08)'::tstzrange; + -- should fail + select '[2010-01-01 01:00:00 -08, 2010-01-01 02:00:00 -05)'::tstzrange; + set timezone to default; + + -- + -- Test user-defined range of floats + -- + + create type float8range as range (subtype=float8); + select '[123.001, 5.e9)'::float8range @> 888.882::float8; + create table float8range_test(f8r float8range, i int); + insert into float8range_test values(range((-100.00007)::float8, '1.111113e9'::float8)); + select * from float8range_test; + drop table float8range_test; + + -- + -- Test rangetypes over domains + -- + + create domain mydomain as int4; + create type mydomainrange as range(subtype=mydomain); + select '[4,50)'::mydomainrange @> 7::mydomain; + + -- + -- Test collations + -- + + create type textrange_c as range(subtype=text, collation="C"); + select '[a, Z)'::textrange_c @> 'b'::text; + drop type textrange_c; + + create type textrange_en_us as range(subtype=text, collation="en_US"); + select '[a, Z)'::textrange_en_us @> 'b'::text; + drop type textrange_en_us; *** a/src/test/regress/sql/type_sanity.sql --- b/src/test/regress/sql/type_sanity.sql *************** *** 20,26 **** SELECT p1.oid, p1.typname FROM pg_type as p1 WHERE p1.typnamespace = 0 OR (p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR ! (p1.typtype not in ('b', 'c', 'd', 'e', 'p')) OR NOT p1.typisdefined OR (p1.typalign not in ('c', 's', 'i', 'd')) OR (p1.typstorage not in ('p', 'x', 'e', 'm')); --- 20,26 ---- FROM pg_type as p1 WHERE p1.typnamespace = 0 OR (p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR ! (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r')) OR NOT p1.typisdefined OR (p1.typalign not in ('c', 's', 'i', 'd')) OR (p1.typstorage not in ('p', 'x', 'e', 'm'));