*** a/doc/src/sgml/datatype.sgml
--- b/doc/src/sgml/datatype.sgml
***************
*** 4131,4136 **** SET xmloption TO { DOCUMENT | CONTENT };
--- 4131,4138 ----
&rowtypes;
+ &rangetypes;
+
Object Identifier Types
*** 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
***************
*** 10426,10431 **** SELECT NULLIF(value, '(none)') ...
--- 10426,10889 ----
+
+ Range Functions and Operators
+
+
+ shows the operators
+ available for range types.
+
+
+
+ Range Operators
+
+
+
+ Operator
+ Description
+ Example
+ Result
+
+
+
+
+ =
+ equal
+ range(1,5) = '[1,4]'::intrange
+ 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)'::period @> '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?
+ '-'::intrange !?
+ 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
*** /dev/null
--- b/doc/src/sgml/rangetypes.sgml
***************
*** 0 ****
--- 1,709 ----
+
+
+
+ Range Types
+
+
+ array
+
+
+
+ PostgreSQL allows columns of a table to be
+ defined as variable-length multidimensional arrays. Arrays of any
+ built-in or user-defined base type, enum type, or composite type
+ can be created.
+ Arrays of domains are not yet supported.
+
+
+
+ Declaration of Array Types
+
+
+ array
+ declaration
+
+
+
+ To illustrate the use of array types, we create this table:
+
+ CREATE TABLE sal_emp (
+ name text,
+ pay_by_quarter integer[],
+ schedule text[][]
+ );
+
+ As shown, an array data type is named by appending square brackets
+ ([]>) to the data type name of the array elements. The
+ above command will create a table named
+ sal_emp with a column of type
+ text (name), a
+ one-dimensional array of type integer
+ (pay_by_quarter), which represents the
+ employee's salary by quarter, and a two-dimensional array of
+ text (schedule), which
+ represents the employee's weekly schedule.
+
+
+
+ The syntax for CREATE TABLE allows the exact size of
+ arrays to be specified, for example:
+
+
+ CREATE TABLE tictactoe (
+ squares integer[3][3]
+ );
+
+
+ However, the current implementation ignores any supplied array size
+ limits, i.e., the behavior is the same as for arrays of unspecified
+ length.
+
+
+
+ The current implementation does not enforce the declared
+ number of dimensions either. Arrays of a particular element type are
+ all considered to be of the same type, regardless of size or number
+ of dimensions. So, declaring the array size or number of dimensions in
+ CREATE TABLE is simply documentation; it does not
+ affect run-time behavior.
+
+
+
+ An alternative syntax, which conforms to the SQL standard by using
+ the keyword ARRAY>, can be used for one-dimensional arrays.
+ pay_by_quarter could have been defined
+ as:
+
+ pay_by_quarter integer ARRAY[4],
+
+ Or, if no array size is to be specified:
+
+ pay_by_quarter integer ARRAY,
+
+ As before, however, PostgreSQL> does not enforce the
+ size restriction in any case.
+
+
+
+
+ Array Value Input
+
+
+ array
+ constant
+
+
+
+ To write an array value as a literal constant, enclose the element
+ values within curly braces and separate them by commas. (If you
+ know C, this is not unlike the C syntax for initializing
+ structures.) You can put double quotes around any element value,
+ and must do so if it contains commas or curly braces. (More
+ details appear below.) Thus, the general format of an array
+ constant is the following:
+
+ '{ val1 delim val2 delim ... }'
+
+ where delim is the delimiter character
+ for the type, as recorded in its pg_type entry.
+ Among the standard data types provided in the
+ PostgreSQL distribution, all use a comma
+ (,>), except for type box> which uses a semicolon
+ (;>). Each val is
+ either a constant of the array element type, or a subarray. An example
+ of an array constant is:
+
+ '{{1,2,3},{4,5,6},{7,8,9}}'
+
+ This constant is a two-dimensional, 3-by-3 array consisting of
+ three subarrays of integers.
+
+
+
+ To set an element of an array constant to NULL, write NULL>
+ for the element value. (Any upper- or lower-case variant of
+ NULL> will do.) If you want an actual string value
+ NULL>, you must put double quotes around it.
+
+
+
+ (These kinds of array constants are actually only a special case of
+ the generic type constants discussed in . The constant is initially
+ treated as a string and passed to the array input conversion
+ routine. An explicit type specification might be necessary.)
+
+
+
+ Now we can show some INSERT statements:
+
+
+ INSERT INTO sal_emp
+ VALUES ('Bill',
+ '{10000, 10000, 10000, 10000}',
+ '{{"meeting", "lunch"}, {"training", "presentation"}}');
+
+ INSERT INTO sal_emp
+ VALUES ('Carol',
+ '{20000, 25000, 25000, 25000}',
+ '{{"breakfast", "consulting"}, {"meeting", "lunch"}}');
+
+
+
+
+ The result of the previous two inserts looks like this:
+
+
+ SELECT * FROM sal_emp;
+ name | pay_by_quarter | schedule
+ -------+---------------------------+-------------------------------------------
+ Bill | {10000,10000,10000,10000} | {{meeting,lunch},{training,presentation}}
+ Carol | {20000,25000,25000,25000} | {{breakfast,consulting},{meeting,lunch}}
+ (2 rows)
+
+
+
+
+ Multidimensional arrays must have matching extents for each
+ dimension. A mismatch causes an error, for example:
+
+
+ INSERT INTO sal_emp
+ VALUES ('Bill',
+ '{10000, 10000, 10000, 10000}',
+ '{{"meeting", "lunch"}, {"meeting"}}');
+ ERROR: multidimensional arrays must have array expressions with matching dimensions
+
+
+
+
+ The ARRAY> constructor syntax can also be used:
+
+ INSERT INTO sal_emp
+ VALUES ('Bill',
+ ARRAY[10000, 10000, 10000, 10000],
+ ARRAY[['meeting', 'lunch'], ['training', 'presentation']]);
+
+ INSERT INTO sal_emp
+ VALUES ('Carol',
+ ARRAY[20000, 25000, 25000, 25000],
+ ARRAY[['breakfast', 'consulting'], ['meeting', 'lunch']]);
+
+ Notice that the array elements are ordinary SQL constants or
+ expressions; for instance, string literals are single quoted, instead of
+ double quoted as they would be in an array literal. The ARRAY>
+ constructor syntax is discussed in more detail in
+ .
+
+
+
+
+ Accessing Arrays
+
+
+ array
+ accessing
+
+
+
+ Now, we can run some queries on the table.
+ First, we show how to access a single element of an array.
+ This query retrieves the names of the employees whose pay changed in
+ the second quarter:
+
+
+ SELECT name FROM sal_emp WHERE pay_by_quarter[1] <> pay_by_quarter[2];
+
+ name
+ -------
+ Carol
+ (1 row)
+
+
+ The array subscript numbers are written within square brackets.
+ By default PostgreSQL uses a
+ one-based numbering convention for arrays, that is,
+ an array of n> elements starts with array[1] and
+ ends with array[n>].
+
+
+
+ This query retrieves the third quarter pay of all employees:
+
+
+ SELECT pay_by_quarter[3] FROM sal_emp;
+
+ pay_by_quarter
+ ----------------
+ 10000
+ 25000
+ (2 rows)
+
+
+
+
+ We can also access arbitrary rectangular slices of an array, or
+ subarrays. An array slice is denoted by writing
+ lower-bound:upper-bound
+ for one or more array dimensions. For example, this query retrieves the first
+ item on Bill's schedule for the first two days of the week:
+
+
+ SELECT schedule[1:2][1:1] FROM sal_emp WHERE name = 'Bill';
+
+ schedule
+ ------------------------
+ {{meeting},{training}}
+ (1 row)
+
+
+ If any dimension is written as a slice, i.e., contains a colon, then all
+ dimensions are treated as slices. Any dimension that has only a single
+ number (no colon) is treated as being from 1>
+ to the number specified. For example, [2]> is treated as
+ [1:2]>, as in this example:
+
+
+ SELECT schedule[1:2][2] FROM sal_emp WHERE name = 'Bill';
+
+ schedule
+ -------------------------------------------
+ {{meeting,lunch},{training,presentation}}
+ (1 row)
+
+
+ To avoid confusion with the non-slice case, it's best to use slice syntax
+ for all dimensions, e.g., [1:2][1:1]>, not [2][1:1]>.
+
+
+
+ An array subscript expression will return null if either the array itself or
+ any of the subscript expressions are null. Also, null is returned if a
+ subscript is outside the array bounds (this case does not raise an error).
+ For example, if schedule>
+ currently has the dimensions [1:3][1:2]> then referencing
+ schedule[3][3]> yields NULL. Similarly, an array reference
+ with the wrong number of subscripts yields a null rather than an error.
+
+
+
+ An array slice expression likewise yields null if the array itself or
+ any of the subscript expressions are null. However, in other
+ cases such as selecting an array slice that
+ is completely outside the current array bounds, a slice expression
+ yields an empty (zero-dimensional) array instead of null. (This
+ does not match non-slice behavior and is done for historical reasons.)
+ If the requested slice partially overlaps the array bounds, then it
+ is silently reduced to just the overlapping region instead of
+ returning null.
+
+
+
+ The current dimensions of any array value can be retrieved with the
+ array_dims function:
+
+
+ SELECT array_dims(schedule) FROM sal_emp WHERE name = 'Carol';
+
+ array_dims
+ ------------
+ [1:2][1:2]
+ (1 row)
+
+
+ array_dims produces a text result,
+ which is convenient for people to read but perhaps inconvenient
+ for programs. Dimensions can also be retrieved with
+ array_upper and array_lower,
+ which return the upper and lower bound of a
+ specified array dimension, respectively:
+
+
+ SELECT array_upper(schedule, 1) FROM sal_emp WHERE name = 'Carol';
+
+ array_upper
+ -------------
+ 2
+ (1 row)
+
+
+ array_length will return the length of a specified
+ array dimension:
+
+
+ SELECT array_length(schedule, 1) FROM sal_emp WHERE name = 'Carol';
+
+ array_length
+ --------------
+ 2
+ (1 row)
+
+
+
+
+
+ Modifying Arrays
+
+
+ array
+ modifying
+
+
+
+ An array value can be replaced completely:
+
+
+ UPDATE sal_emp SET pay_by_quarter = '{25000,25000,27000,27000}'
+ WHERE name = 'Carol';
+
+
+ or using the ARRAY expression syntax:
+
+
+ UPDATE sal_emp SET pay_by_quarter = ARRAY[25000,25000,27000,27000]
+ WHERE name = 'Carol';
+
+
+ An array can also be updated at a single element:
+
+
+ UPDATE sal_emp SET pay_by_quarter[4] = 15000
+ WHERE name = 'Bill';
+
+
+ or updated in a slice:
+
+
+ UPDATE sal_emp SET pay_by_quarter[1:2] = '{27000,27000}'
+ WHERE name = 'Carol';
+
+
+
+
+
+ A stored array value can be enlarged by assigning to elements not already
+ present. Any positions between those previously present and the newly
+ assigned elements will be filled with nulls. For example, if array
+ myarray> currently has 4 elements, it will have six
+ elements after an update that assigns to myarray[6]>;
+ myarray[5]> will contain null.
+ Currently, enlargement in this fashion is only allowed for one-dimensional
+ arrays, not multidimensional arrays.
+
+
+
+ Subscripted assignment allows creation of arrays that do not use one-based
+ subscripts. For example one might assign to myarray[-2:7]> to
+ create an array with subscript values from -2 to 7.
+
+
+
+ New array values can also be constructed using the concatenation operator,
+ ||:
+
+ SELECT ARRAY[1,2] || ARRAY[3,4];
+ ?column?
+ -----------
+ {1,2,3,4}
+ (1 row)
+
+ SELECT ARRAY[5,6] || ARRAY[[1,2],[3,4]];
+ ?column?
+ ---------------------
+ {{5,6},{1,2},{3,4}}
+ (1 row)
+
+
+
+
+ The concatenation operator allows a single element to be pushed onto the
+ beginning or end of a one-dimensional array. It also accepts two
+ N>-dimensional arrays, or an N>-dimensional
+ and an N+1>-dimensional array.
+
+
+
+ When a single element is pushed onto either the beginning or end of a
+ one-dimensional array, the result is an array with the same lower bound
+ subscript as the array operand. For example:
+
+ SELECT array_dims(1 || '[0:1]={2,3}'::int[]);
+ array_dims
+ ------------
+ [0:2]
+ (1 row)
+
+ SELECT array_dims(ARRAY[1,2] || 3);
+ array_dims
+ ------------
+ [1:3]
+ (1 row)
+
+
+
+
+ When two arrays with an equal number of dimensions are concatenated, the
+ result retains the lower bound subscript of the left-hand operand's outer
+ dimension. The result is an array comprising every element of the left-hand
+ operand followed by every element of the right-hand operand. For example:
+
+ SELECT array_dims(ARRAY[1,2] || ARRAY[3,4,5]);
+ array_dims
+ ------------
+ [1:5]
+ (1 row)
+
+ SELECT array_dims(ARRAY[[1,2],[3,4]] || ARRAY[[5,6],[7,8],[9,0]]);
+ array_dims
+ ------------
+ [1:5][1:2]
+ (1 row)
+
+
+
+
+ When an N>-dimensional array is pushed onto the beginning
+ or end of an N+1>-dimensional array, the result is
+ analogous to the element-array case above. Each N>-dimensional
+ sub-array is essentially an element of the N+1>-dimensional
+ array's outer dimension. For example:
+
+ SELECT array_dims(ARRAY[1,2] || ARRAY[[3,4],[5,6]]);
+ array_dims
+ ------------
+ [1:3][1:2]
+ (1 row)
+
+
+
+
+ An array can also be constructed by using the functions
+ array_prepend, array_append,
+ or array_cat. The first two only support one-dimensional
+ arrays, but array_cat supports multidimensional arrays.
+
+ Note that the concatenation operator discussed above is preferred over
+ direct use of these functions. In fact, these functions primarily exist for use
+ in implementing the concatenation operator. However, they might be directly
+ useful in the creation of user-defined aggregates. Some examples:
+
+
+ SELECT array_prepend(1, ARRAY[2,3]);
+ array_prepend
+ ---------------
+ {1,2,3}
+ (1 row)
+
+ SELECT array_append(ARRAY[1,2], 3);
+ array_append
+ --------------
+ {1,2,3}
+ (1 row)
+
+ SELECT array_cat(ARRAY[1,2], ARRAY[3,4]);
+ array_cat
+ -----------
+ {1,2,3,4}
+ (1 row)
+
+ SELECT array_cat(ARRAY[[1,2],[3,4]], ARRAY[5,6]);
+ array_cat
+ ---------------------
+ {{1,2},{3,4},{5,6}}
+ (1 row)
+
+ SELECT array_cat(ARRAY[5,6], ARRAY[[1,2],[3,4]]);
+ array_cat
+ ---------------------
+ {{5,6},{1,2},{3,4}}
+
+
+
+
+
+ Searching in Arrays
+
+
+ array
+ searching
+
+
+
+ To search for a value in an array, each value must be checked.
+ This can be done manually, if you know the size of the array.
+ For example:
+
+
+ SELECT * FROM sal_emp WHERE pay_by_quarter[1] = 10000 OR
+ pay_by_quarter[2] = 10000 OR
+ pay_by_quarter[3] = 10000 OR
+ pay_by_quarter[4] = 10000;
+
+
+ However, this quickly becomes tedious for large arrays, and is not
+ helpful if the size of the array is unknown. An alternative method is
+ described in . The above
+ query could be replaced by:
+
+
+ SELECT * FROM sal_emp WHERE 10000 = ANY (pay_by_quarter);
+
+
+ In addition, you can find rows where the array has all values
+ equal to 10000 with:
+
+
+ SELECT * FROM sal_emp WHERE 10000 = ALL (pay_by_quarter);
+
+
+
+
+
+ Alternatively, the generate_subscripts> function can be used.
+ For example:
+
+
+ SELECT * FROM
+ (SELECT pay_by_quarter,
+ generate_subscripts(pay_by_quarter, 1) AS s
+ FROM sal_emp) AS foo
+ WHERE pay_by_quarter[s] = 10000;
+
+
+ This function is described in .
+
+
+
+
+ Arrays are not sets; searching for specific array elements
+ can be a sign of database misdesign. Consider
+ using a separate table with a row for each item that would be an
+ array element. This will be easier to search, and is likely to
+ scale better for a large number of elements.
+
+
+
+
+
+ Array Input and Output Syntax
+
+
+ array
+ I/O
+
+
+
+ The external text representation of an array value consists of items that
+ are interpreted according to the I/O conversion rules for the array's
+ element type, plus decoration that indicates the array structure.
+ The decoration consists of curly braces ({> and }>)
+ around the array value plus delimiter characters between adjacent items.
+ The delimiter character is usually a comma (,>) but can be
+ something else: it is determined by the typdelim> setting
+ for the array's element type. Among the standard data types provided
+ in the PostgreSQL distribution, all use a comma,
+ except for type box>, which uses a semicolon (;>).
+ In a multidimensional array, each dimension (row, plane,
+ cube, etc.) gets its own level of curly braces, and delimiters
+ must be written between adjacent curly-braced entities of the same level.
+
+
+
+ The array output routine will put double quotes around element values
+ if they are empty strings, contain curly braces, delimiter characters,
+ double quotes, backslashes, or white space, or match the word
+ NULL>. Double quotes and backslashes
+ embedded in element values will be backslash-escaped. For numeric
+ data types it is safe to assume that double quotes will never appear, but
+ for textual data types one should be prepared to cope with either the presence
+ or absence of quotes.
+
+
+
+ By default, the lower bound index value of an array's dimensions is
+ set to one. To represent arrays with other lower bounds, the array
+ subscript ranges can be specified explicitly before writing the
+ array contents.
+ This decoration consists of square brackets ([]>)
+ around each array dimension's lower and upper bounds, with
+ a colon (:>) delimiter character in between. The
+ array dimension decoration is followed by an equal sign (=>).
+ For example:
+
+ SELECT f1[1][-2][3] AS e1, f1[1][-1][5] AS e2
+ FROM (SELECT '[1:1][-2:-1][3:5]={{{1,2,3},{4,5,6}}}'::int[] AS f1) AS ss;
+
+ e1 | e2
+ ----+----
+ 1 | 6
+ (1 row)
+
+ The array output routine will include explicit dimensions in its result
+ only when there are one or more lower bounds different from one.
+
+
+
+ If the value written for an element is NULL> (in any case
+ variant), the element is taken to be NULL. The presence of any quotes
+ or backslashes disables this and allows the literal string value
+ NULL> to be entered. Also, for backwards compatibility with
+ pre-8.2 versions of PostgreSQL>, the configuration parameter can be turned
+ off> to suppress recognition of NULL> as a NULL.
+
+
+
+ As shown previously, when writing an array value you can use double
+ quotes around any individual array element. You must> do so
+ if the element value would otherwise confuse the array-value parser.
+ For example, elements containing curly braces, commas (or the data type's
+ delimiter character), double quotes, backslashes, or leading or trailing
+ whitespace must be double-quoted. Empty strings and strings matching the
+ word NULL> must be quoted, too. To put a double quote or
+ backslash in a quoted array element value, use escape string syntax
+ and precede it with a backslash. Alternatively, you can avoid quotes and use
+ backslash-escaping to protect all data characters that would otherwise
+ be taken as array syntax.
+
+
+
+ You can add whitespace before a left brace or after a right
+ brace. You can also add whitespace before or after any individual item
+ string. In all of these cases the whitespace will be ignored. However,
+ whitespace within double-quoted elements, or surrounded on both sides by
+ non-whitespace characters of an element, is not ignored.
+
+
+
+
+ Remember that what you write in an SQL command will first be interpreted
+ as a string literal, and then as an array. This doubles the number of
+ backslashes you need. For example, to insert a text> array
+ value containing a backslash and a double quote, you'd need to write:
+
+ INSERT ... VALUES (E'{"\\\\","\\""}');
+
+ The escape string processor removes one level of backslashes, so that
+ what arrives at the array-value parser looks like {"\\","\""}>.
+ In turn, the strings fed to the text> data type's input routine
+ become \> and "> respectively. (If we were working
+ with a data type whose input routine also treated backslashes specially,
+ bytea> for example, we might need as many as eight backslashes
+ in the command to get one backslash into the stored array element.)
+ Dollar quoting (see ) can be
+ used to avoid the need to double backslashes.
+
+
+
+
+
+ The ARRAY> constructor syntax (see
+ ) is often easier to work
+ with than the array-literal syntax when writing array values in SQL
+ commands. In ARRAY>, individual element values are written the
+ same way they would be written when not members of an array.
+
+
+
+
+
*** 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_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_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_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \
pg_foreign_table.h \
! pg_default_acl.h pg_seclabel.h \
toasting.h indexing.h \
)
--- 39,45 ----
pg_ts_parser.h pg_ts_template.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_range.h\
toasting.h indexing.h \
)
*** a/src/backend/catalog/pg_proc.c
--- b/src/backend/catalog/pg_proc.c
***************
*** 165,170 **** ProcedureCreate(const char *procedureName,
--- 165,171 ----
case ANYELEMENTOID:
case ANYNONARRAYOID:
case ANYENUMOID:
+ case ANYRANGEOID:
genericInParam = true;
break;
case INTERNALOID:
***************
*** 189,194 **** ProcedureCreate(const char *procedureName,
--- 190,196 ----
case ANYELEMENTOID:
case ANYNONARRAYOID:
case ANYENUMOID:
+ case ANYRANGEOID:
genericOutParam = true;
break;
case INTERNALOID:
*** /dev/null
--- b/src/backend/catalog/pg_range.c
***************
*** 0 ****
--- 1,122 ----
+ /*-------------------------------------------------------------------------
+ *
+ * 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_proc.h"
+ #include "catalog/pg_range.h"
+ #include "catalog/pg_type.h"
+ #include "utils/fmgroids.h"
+ #include "utils/tqual.h"
+
+ /*
+ * RangeCreate
+ * Create an entry in pg_range.
+ */
+ void
+ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, regproc rangeSubtypeCmp,
+ 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_rngsubcmp - 1] = ObjectIdGetDatum(rangeSubtypeCmp);
+ 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 = ProcedureRelationId;
+ referenced.objectId = rangeSubtypeCmp;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+ if (OidIsValid(rangeCanonical))
+ {
+ referenced.classId = ProcedureRelationId;
+ referenced.objectId = rangeCanonical;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
+
+ if (OidIsValid(rangeSubFloat))
+ {
+ referenced.classId = ProcedureRelationId;
+ referenced.objectId = rangeSubFloat;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
+
+ heap_close(pg_range, RowExclusiveLock);
+ }
+
+
+ /*
+ * RangeDelete
+ * Remove the pg_range entry.
+ */
+ void
+ RangeDelete(Oid rangeTypeOid)
+ {
+ Relation pg_range;
+ ScanKeyData key[1];
+ SysScanDesc scan;
+ HeapTuple tup;
+
+ pg_range = heap_open(RangeRelationId, RowExclusiveLock);
+
+ ScanKeyInit(&key[0],
+ Anum_pg_range_rngtypid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(rangeTypeOid));
+
+ scan = systable_beginscan(pg_range, RangeTypidIndexId, true,
+ SnapshotNow, 1, key);
+
+ while (HeapTupleIsValid(tup = systable_getnext(scan)))
+ {
+ simple_heap_delete(pg_range, &tup->t_self);
+ }
+
+ systable_endscan(scan);
+
+ heap_close(pg_range, RowExclusiveLock);
+ }
*** a/src/backend/commands/typecmds.c
--- b/src/backend/commands/typecmds.c
***************
*** 42,47 ****
--- 42,49 ----
#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"
***************
*** 84,89 **** static Oid findTypeSendFunction(List *procname, Oid typeOid);
--- 86,94 ----
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 findRangeSubtypeCmpFunction(List *procname, Oid typeOid);
+ static Oid findRangeSubtypeFloatFunction(List *procname, Oid typeOid);
static List *get_rels_with_domain(Oid domainOid, LOCKMODE lockmode);
static void checkDomainOwner(HeapTuple tup);
static void checkEnumOwner(HeapTuple tup);
***************
*** 723,728 **** RemoveTypeById(Oid typeOid)
--- 728,741 ----
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);
***************
*** 1194,1199 **** DefineEnum(CreateEnumStmt *stmt)
--- 1207,1461 ----
}
/*
+ * 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 *rangeSubtypeCmpName = NIL;
+ List *rangeSubtypeFloatName = NIL;
+ regproc rangeAnalyze = InvalidOid;
+ Oid rangeSubtype = InvalidOid;
+ regproc rangeSubtypeCmp = 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 base 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, "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_cmp") == 0)
+ {
+ if (rangeSubtypeCmpName != NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options")));
+ rangeSubtypeCmpName = 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 (rangeSubtypeCmpName == NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("type attribute \"subtype_cmp\" is required")));
+
+ rangeSubtypeCmp = findRangeSubtypeCmpFunction(
+ rangeSubtypeCmpName, 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 */
+
+ /* create the entry in pg_range */
+ RangeCreate(typoid, rangeSubtype, rangeSubtypeCmp, 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', /* ranges have align i, so do their arrays */
+ 'x', /* ARRAY is always toastable */
+ -1, /* typMod (Domains only) */
+ 0, /* Array dimensions of typbasetype */
+ false); /* Type NOT NULL */
+
+ pfree(rangeArrayName);
+ }
+
+ /*
* AlterEnum
* Adds a new label to an existing enum.
*/
***************
*** 1509,1514 **** findTypeAnalyzeFunction(List *procname, Oid typeOid)
--- 1771,1893 ----
}
/*
+ * Used to find comparison function that operates on a range's subtype.
+ */
+ static Oid
+ findRangeSubtypeCmpFunction(List *procname, Oid subType)
+ {
+ Oid argList[2];
+ Oid procOid;
+ FuncCandidateList best_candidate;
+ FuncCandidateList raw_candidates;
+ FuncCandidateList cur_candidates;
+
+ /*
+ * Binary functions take two arguments of type 'typeOid'.
+ */
+ argList[0] = subType;
+ argList[1] = subType;
+
+ /*
+ * Find a matching function. We're more permissive regarding the
+ * subtype_cmp function.
+ */
+ raw_candidates = FuncnameGetCandidates(procname, 2, NIL, false, false);
+ func_match_argtypes(2, argList, raw_candidates, &cur_candidates);
+ best_candidate = func_select_candidate(2, argList, cur_candidates);
+
+ if (best_candidate == NULL || !OidIsValid(best_candidate->oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("function %s does not exist",
+ func_signature_string(procname, 2, NIL, argList))));
+
+ procOid = best_candidate->oid;
+
+ if (get_func_rettype(procOid) != INT4OID)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("range subtype comparison function %s must return type \"integer\"",
+ func_signature_string(procname, 2, NIL, argList))));
+
+ if (func_volatile(procOid) != PROVOLATILE_IMMUTABLE)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("range subtype comparison function %s must be immutable",
+ func_signature_string(procname, 2, NIL, argList))));
+
+ return procOid;
+ }
+
+ /*
+ * Used to find a range's 'canonical' function.
+ */
+ static Oid
+ findRangeSubtypeFloatFunction(List *procname, Oid typeOid)
+ {
+ Oid argList[1];
+ Oid procOid;
+
+ argList[0] = typeOid;
+
+ procOid = LookupFuncName(procname, 1, argList, true);
+
+ if (!OidIsValid(procOid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("function %s does not exist",
+ func_signature_string(procname, 1, NIL, argList))));
+
+ if (get_func_rettype(procOid) != FLOAT8OID)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("range subtype float function %s must return type \"float8\"",
+ func_signature_string(procname, 1, NIL, argList))));
+
+ if (func_volatile(procOid) != PROVOLATILE_IMMUTABLE)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("range subtype float function %s must be immutable",
+ func_signature_string(procname, 1, NIL, argList))));
+
+ return procOid;
+ }
+
+ /*
+ * Used to find a range's 'canonical' function.
+ */
+ static Oid
+ findRangeCanonicalFunction(List *procname, Oid typeOid)
+ {
+ Oid argList[1];
+ Oid procOid;
+
+ argList[0] = typeOid;
+
+ procOid = LookupFuncName(procname, 1, argList, true);
+
+ if (!OidIsValid(procOid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("function %s does not exist",
+ func_signature_string(procname, 1, NIL, argList))));
+
+ if (get_func_rettype(procOid) != typeOid)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("range canonical function %s must return range type",
+ NameListToString(procname))));
+
+ if (func_volatile(procOid) != PROVOLATILE_IMMUTABLE)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("range canonical function %s must be immutable",
+ func_signature_string(procname, 1, NIL, argList))));
+
+ return procOid;
+ }
+
+ /*
* AssignTypeArrayOid
*
* Pre-assign the type's array OID for use in pg_type.typarray
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
***************
*** 2933,2938 **** _copyCreateEnumStmt(CreateEnumStmt *from)
--- 2933,2949 ----
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)
{
***************
*** 4129,4134 **** copyObject(void *from)
--- 4140,4148 ----
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
***************
*** 1406,1411 **** _equalCreateEnumStmt(CreateEnumStmt *a, CreateEnumStmt *b)
--- 1406,1420 ----
}
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);
***************
*** 2742,2747 **** equal(void *a, void *b)
--- 2751,2759 ----
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
***************
*** 3908,3913 **** DefineStmt:
--- 3908,3920 ----
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
***************
*** 142,147 **** coerce_type(ParseState *pstate, Node *node,
--- 142,148 ----
targetTypeId == ANYELEMENTOID ||
targetTypeId == ANYNONARRAYOID ||
(targetTypeId == ANYARRAYOID && inputTypeId != UNKNOWNOID) ||
+ (targetTypeId == ANYRANGEOID && inputTypeId != UNKNOWNOID) ||
(targetTypeId == ANYENUMOID && inputTypeId != UNKNOWNOID))
{
/*
***************
*** 1246,1251 **** check_generic_type_consistency(Oid *actual_arg_types,
--- 1247,1254 ----
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;
***************
*** 1282,1287 **** check_generic_type_consistency(Oid *actual_arg_types,
--- 1285,1298 ----
return false;
array_typeid = actual_type;
}
+ else if (decl_type == ANYRANGEOID)
+ {
+ if (actual_type == UNKNOWNOID)
+ continue;
+ 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 */
***************
*** 1327,1332 **** check_generic_type_consistency(Oid *actual_arg_types,
--- 1338,1364 ----
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 an array, 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;
}
***************
*** 1403,1409 **** enforce_generic_type_consistency(Oid *actual_arg_types,
--- 1435,1443 ----
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);
***************
*** 1463,1468 **** enforce_generic_type_consistency(Oid *actual_arg_types,
--- 1497,1521 ----
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 */
+ 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;
+ }
}
/*
***************
*** 1508,1513 **** enforce_generic_type_consistency(Oid *actual_arg_types,
--- 1561,1594 ----
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)
***************
*** 1574,1579 **** enforce_generic_type_consistency(Oid *actual_arg_types,
--- 1655,1673 ----
}
declared_arg_types[j] = array_typeid;
}
+ else if (decl_type == ANYRANGEOID)
+ {
+ if (!OidIsValid(range_typeid))
+ {
+ range_typeid = get_range_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;
+ }
}
}
***************
*** 1592,1597 **** enforce_generic_type_consistency(Oid *actual_arg_types,
--- 1686,1706 ----
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 ||
***************
*** 1636,1642 **** 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);
--- 1745,1752 ----
}
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);
***************
*** 1651,1657 **** resolve_generic_type(Oid declared_type,
}
else if (declared_type == ANYELEMENTOID ||
declared_type == ANYNONARRAYOID ||
! declared_type == ANYENUMOID)
{
if (context_declared_type == ANYARRAYOID)
{
--- 1761,1768 ----
}
else if (declared_type == ANYELEMENTOID ||
declared_type == ANYNONARRAYOID ||
! declared_type == ANYENUMOID ||
! declared_type == ANYRANGEOID)
{
if (context_declared_type == ANYARRAYOID)
{
***************
*** 1665,1670 **** resolve_generic_type(Oid declared_type,
--- 1776,1793 ----
format_type_be(context_actual_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)
***************
*** 1778,1783 **** IsBinaryCoercible(Oid srctype, Oid targettype)
--- 1901,1911 ----
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
***************
*** 190,195 **** check_xact_readonly(Node *parsetree)
--- 190,196 ----
case T_CreateTrigStmt:
case T_CompositeTypeStmt:
case T_CreateEnumStmt:
+ case T_CreateRangeStmt:
case T_AlterEnumStmt:
case T_ViewStmt:
case T_DropCastStmt:
***************
*** 880,885 **** standard_ProcessUtility(Node *parsetree,
--- 881,890 ----
DefineEnum((CreateEnumStmt *) parsetree);
break;
+ case T_CreateRangeStmt:
+ DefineRange((CreateRangeStmt *) parsetree);
+ break;
+
case T_AlterEnumStmt: /* ALTER TYPE (enum) */
/*
* We disallow this in transaction blocks, because we can't cope
***************
*** 1937,1942 **** CreateCommandTag(Node *parsetree)
--- 1942,1951 ----
tag = "CREATE TYPE";
break;
+ case T_CreateRangeStmt:
+ tag = "CREATE TYPE";
+ break;
+
case T_AlterEnumStmt:
tag = "ALTER TYPE";
break;
***************
*** 2484,2489 **** GetCommandLogLevel(Node *parsetree)
--- 2493,2502 ----
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,1581 ----
+ /*-------------------------------------------------------------------------
+ *
+ * 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 "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/typcache.h"
+
+ /* 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_LB,
+ RANGE_PSTATE_UB,
+ 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);
+
+ /*
+ *----------------------------------------------------------
+ * 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 = palloc0(sizeof(RangeBound));
+ RangeBound *upper = palloc0(sizeof(RangeBound));
+
+ 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;
+ char *ubound_str;
+ 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 = palloc0(sizeof(RangeBound));
+ RangeBound *upper = palloc0(sizeof(RangeBound));
+
+ if (PG_ARGISNULL(0))
+ elog(ERROR, "NULL range boundaries are not supported");
+
+ lower->rngtypid = rngtypid;
+ lower->inclusive = true;
+ lower->val = arg;
+ lower->lower = true;
+ upper->rngtypid = rngtypid;
+ upper->inclusive = true;
+ upper->val = arg;
+ upper->lower = 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 = palloc0(sizeof(RangeBound));
+ RangeBound *upper = palloc0(sizeof(RangeBound));
+
+ if (PG_ARGISNULL(0))
+ elog(ERROR, "NULL range boundaries are not supported");
+
+ lower->rngtypid = rngtypid;
+ lower->inclusive = false;
+ lower->infinite = true;
+ lower->lower = true;
+
+ upper->rngtypid = rngtypid;
+ upper->inclusive = false;
+ upper->val = arg;
+ upper->lower = false;
+
+ 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 = palloc0(sizeof(RangeBound));
+ RangeBound *upper = palloc0(sizeof(RangeBound));
+
+ if (PG_ARGISNULL(0))
+ elog(ERROR, "NULL range boundaries are not supported");
+
+ lower->rngtypid = rngtypid;
+ lower->inclusive = false;
+ lower->val = arg;
+ lower->lower = true;
+
+ upper->rngtypid = rngtypid;
+ upper->inclusive = false;
+ upper->infinite = true;
+ upper->lower = false;
+
+ 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 = palloc0(sizeof(RangeBound));
+ RangeBound *upper = palloc0(sizeof(RangeBound));
+
+ if (PG_ARGISNULL(0))
+ elog(ERROR, "NULL range boundaries are not supported");
+
+ lower->rngtypid = rngtypid;
+ lower->inclusive = false;
+ lower->infinite = true;
+ lower->lower = true;
+
+ upper->rngtypid = rngtypid;
+ upper->inclusive = true;
+ upper->val = arg;
+ upper->lower = false;
+
+ 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 = palloc0(sizeof(RangeBound));
+ RangeBound *upper = palloc0(sizeof(RangeBound));
+
+ if (PG_ARGISNULL(0))
+ elog(ERROR, "NULL range boundaries are not supported");
+
+ lower->rngtypid = rngtypid;
+ lower->inclusive = true;
+ lower->val = arg;
+ lower->lower = true;
+ upper->rngtypid = rngtypid;
+ upper->inclusive = false;
+ upper->infinite = true;
+ upper->lower = false;
+
+ 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);
+ Oid rngtypid = get_fn_expr_argtype(fcinfo->flinfo,0);
+ RangeBound lower;
+ RangeBound upper;
+
+ lower.rngtypid = rngtypid;
+ lower.inclusive = true;
+ lower.infinite = false;
+ lower.lower = true;
+ lower.val = val;
+
+ upper.rngtypid = rngtypid;
+ upper.inclusive = true;
+ upper.infinite = false;
+ upper.lower = false;
+ upper.val = val;
+
+ r2 = DatumGetRangeType(make_range(&lower, &upper, 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);
+ Oid rngtypid = get_fn_expr_argtype(fcinfo->flinfo,1);
+ RangeBound lower;
+ RangeBound upper;
+
+ lower.rngtypid = rngtypid;
+ lower.inclusive = true;
+ lower.infinite = false;
+ lower.lower = true;
+ lower.val = val;
+
+ upper.rngtypid = rngtypid;
+ upper.inclusive = true;
+ upper.infinite = false;
+ upper.lower = false;
+ upper.val = val;
+
+ r2 = DatumGetRangeType(make_range(&lower, &upper, 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;
+
+ Oid cmpFn;
+
+ 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.
+ */
+
+ cmpFn = get_range_subtype_cmp(lower1.rngtypid);
+
+ if (lower1.inclusive != upper2.inclusive)
+ {
+ if (DatumGetInt32(OidFunctionCall2(cmpFn, lower1.val, upper2.val)) == 0)
+ PG_RETURN_BOOL(true);
+ }
+
+ if (upper1.inclusive != lower2.inclusive)
+ {
+ if (DatumGetInt32(OidFunctionCall2(cmpFn, 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.
+ */
+ InitFunctionCallInfoData(locfcinfo, &typentry->hash_proc_finfo, 1,
+ 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
+ intrange_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
+ 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
+ *----------------------------------------------------------
+ */
+
+ /*
+ * 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;
+ char *ptr;
+ Oid subtype = get_range_subtype(lower->rngtypid);
+ int16 typlen = get_typlen(subtype);
+ char typalign = get_typalign(subtype);
+ char typbyval = get_typbyval(subtype);
+ size_t llen;
+ size_t ulen;
+ 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);
+ msize += sizeof(char);
+
+ if (RANGE_HAS_LBOUND(flags))
+ {
+ llen = att_addlength_datum(0, typlen, lower->val);
+ msize = att_align_nominal(msize, typalign);
+ msize += llen;
+ }
+
+ if (RANGE_HAS_UBOUND(flags))
+ {
+ ulen = att_addlength_datum(0, typlen, upper->val);
+ msize = att_align_nominal(msize, typalign);
+ msize += ulen;
+ }
+
+ ptr = palloc(msize);
+ range = (Datum) ptr;
+
+ ptr += VARHDRSZ;
+
+ memcpy(ptr, &lower->rngtypid, sizeof(Oid));
+ ptr += sizeof(Oid);
+ memcpy(ptr, &flags, sizeof(char));
+ ptr += sizeof(char);
+
+ if (RANGE_HAS_LBOUND(flags))
+ {
+ Assert(lower->lower);
+ ptr = (char *) att_align_nominal(ptr, typalign);
+ if (typbyval)
+ store_att_byval(ptr, lower->val, typlen);
+ else
+ memcpy(ptr, (void *) lower->val, llen);
+ ptr += llen;
+ }
+
+ if (RANGE_HAS_UBOUND(flags))
+ {
+ Assert(!upper->lower);
+ ptr = (char *) att_align_nominal(ptr, typalign);
+ if (typbyval)
+ store_att_byval(ptr, upper->val, typlen);
+ else
+ memcpy(ptr, (void *) upper->val, ulen);
+ ptr += ulen;
+ }
+
+ SET_VARSIZE(range, msize);
+ PG_RETURN_RANGE(range);
+ }
+
+ void
+ range_deserialize(RangeType *range, RangeBound *lower, RangeBound *upper,
+ bool *empty)
+ {
+ char *ptr = VARDATA(range);
+ int llen;
+ int ulen;
+ char typalign;
+ int16 typlen;
+ int16 typbyval;
+ char flags;
+ Oid rngtypid;
+ Oid subtype;
+ Datum lbound;
+ Datum ubound;
+
+ memset(lower, 0, sizeof(RangeBound));
+ memset(upper, 0, sizeof(RangeBound));
+
+ memcpy(&rngtypid, ptr, sizeof(Oid));
+ ptr += sizeof(Oid);
+ memcpy(&flags, ptr, sizeof(char));
+ ptr += sizeof(char);
+
+ 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 = (char *) att_align_nominal(ptr, typalign);
+ llen = att_addlength_datum(0, typlen, (Datum) ptr);
+ lbound = fetch_att(ptr, typbyval, typlen);
+ ptr += llen;
+ }
+ else
+ lbound = (Datum) 0;
+
+ if (RANGE_HAS_UBOUND(flags))
+ {
+ ptr = (char *) att_align_nominal(ptr, typalign);
+ ulen = att_addlength_datum(0, typlen, (Datum) ptr);
+ ubound = fetch_att(ptr, typbyval, typlen);
+ ptr += ulen;
+ }
+ 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)
+ {
+ regproc cmpFn;
+ int result;
+
+ 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;
+
+ cmpFn = get_range_subtype_cmp(b1->rngtypid);
+ result = DatumGetInt32(OidFunctionCall2(cmpFn, 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
+ *----------------------------------------------------------
+ */
+
+ 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 uidx = 0;
+ 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];
+
+ if((ch == '"' || ch == '\\') && !escape)
+ {
+ if (pstate != RANGE_PSTATE_LB && pstate != RANGE_PSTATE_UB)
+ elog(ERROR, "syntax error on range input, character %d", i);
+ if (ch == '"')
+ {
+ if (pstate == RANGE_PSTATE_LB)
+ lb_quoted = true;
+ else if (pstate == RANGE_PSTATE_UB)
+ ub_quoted = true;
+ inside_quotes = !inside_quotes;
+ }
+ else if (ch == '\\')
+ escape = true;
+ continue;
+ }
+
+ escape = false;
+
+ if(isspace(ch) && !inside_quotes)
+ continue;
+
+ if(ch == '-' && pstate == RANGE_PSTATE_INIT)
+ {
+ fl |= RANGE_EMPTY;
+ pstate = RANGE_PSTATE_DONE;
+ /* read the rest to make sure it's whitespace */
+ continue;
+ }
+
+ if((ch == '[' || ch == '(') && !inside_quotes)
+ {
+ if (pstate != RANGE_PSTATE_INIT)
+ elog(ERROR, "syntax error on range input, character %d", i);
+ if (ch == '[')
+ fl |= RANGE_LB_INC;
+ pstate = RANGE_PSTATE_LB;
+ continue;
+ }
+
+ if((ch == ')' || ch == ']') && !inside_quotes)
+ {
+ if (pstate != RANGE_PSTATE_UB)
+ elog(ERROR, "syntax error on range input, character %d", i);
+ if (ch == ']')
+ fl |= RANGE_UB_INC;
+ pstate = RANGE_PSTATE_DONE;
+ continue;
+ }
+
+ if(ch == ',' && !inside_quotes)
+ {
+ if (pstate != RANGE_PSTATE_LB)
+ elog(ERROR, "syntax error on range input, character %d", i);
+ pstate = RANGE_PSTATE_UB;
+ continue;
+ }
+
+ if (pstate == RANGE_PSTATE_LB)
+ {
+ lb[lidx++] = ch;
+ continue;
+ }
+
+ if (pstate == RANGE_PSTATE_UB)
+ {
+ ub[uidx++] = ch;
+ continue;
+ }
+
+ elog(ERROR, "syntax error on range input: characters after end of input");
+ }
+
+ if (inside_quotes)
+ elog(ERROR, "syntax error on range input: unterminated quotation");
+
+ 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 (empty1 && !empty2)
+ return false;
+ else if (empty2)
+ return true;
+
+ if (range_cmp_bounds(&lower1, &lower2) > 0)
+ return false;
+ if (range_cmp_bounds(&upper1, &upper2) < 0)
+ return false;
+
+ return true;
+ }
*** /dev/null
--- b/src/backend/utils/adt/rangetypes_gist.c
***************
*** 0 ****
--- 1,61 ----
+ /*-------------------------------------------------------------------------
+ *
+ * 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 "utils/rangetypes.h"
+
+ /* GiST support */
+ Datum
+ range_gist_consistent(PG_FUNCTION_ARGS)
+ {
+ PG_RETURN_VOID(); //TODO
+ }
+
+ Datum
+ range_gist_union(PG_FUNCTION_ARGS)
+ {
+ PG_RETURN_VOID(); //TODO
+ }
+
+ Datum
+ range_gist_compress(PG_FUNCTION_ARGS)
+ {
+ PG_RETURN_VOID(); //TODO
+ }
+
+ Datum
+ range_gist_decompress(PG_FUNCTION_ARGS)
+ {
+ PG_RETURN_VOID(); //TODO
+ }
+
+ Datum
+ range_gist_penalty(PG_FUNCTION_ARGS)
+ {
+ PG_RETURN_VOID(); //TODO
+ }
+
+ Datum
+ range_gist_picksplit(PG_FUNCTION_ARGS)
+ {
+ PG_RETURN_VOID(); //TODO
+ }
+
+ Datum
+ range_gist_same(PG_FUNCTION_ARGS)
+ {
+ PG_RETURN_VOID(); //TODO
+ }
+
+
*** 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
***************
*** 25,30 ****
--- 25,31 ----
#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"
***************
*** 1927,1933 **** get_type_io_data(Oid typid,
ReleaseSysCache(typeTuple);
}
- #ifdef NOT_USED
char
get_typalign(Oid typid)
{
--- 1928,1933 ----
***************
*** 1946,1952 **** get_typalign(Oid typid)
else
return 'i';
}
- #endif
char
get_typstorage(Oid typid)
--- 1946,1951 ----
***************
*** 2199,2204 **** type_is_enum(Oid typid)
--- 2198,2213 ----
}
/*
+ * 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.
***************
*** 2767,2769 **** get_namespace_name(Oid nspid)
--- 2776,2873 ----
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_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_subtype_cmp(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->rngsubcmp;
+ 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
***************
*** 42,47 ****
--- 42,48 ----
#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"
***************
*** 531,536 **** static const struct cachedesc cacheinfo[] = {
--- 532,559 ----
},
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
***************
*** 407,416 **** resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
--- 407,418 ----
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;
int i;
/* See if there are any polymorphic outputs; quick out if not */
***************
*** 432,437 **** resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
--- 434,441 ----
have_anyelement_result = true;
have_anyenum = true;
break;
+ case ANYRANGEOID:
+ have_anyrange_result = true;
default:
break;
}
***************
*** 460,465 **** resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
--- 464,473 ----
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;
}
***************
*** 479,484 **** resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
--- 487,501 ----
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;
***************
*** 508,513 **** resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
--- 525,537 ----
-1,
0);
break;
+ case ANYRANGEOID:
+ TupleDescInitEntry(tupdesc, i + 1,
+ NameStr(tupdesc->attrs[i]->attname),
+ anyrange_type,
+ -1,
+ 0);
+ break;
default:
break;
}
***************
*** 547,552 **** resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
--- 571,577 ----
case ANYELEMENTOID:
case ANYNONARRAYOID:
case ANYENUMOID:
+ case ANYRANGEOID:
if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
have_anyelement_result = true;
else
***************
*** 611,616 **** resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
--- 636,642 ----
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
***************
*** 163,168 **** static void dumpNamespace(Archive *fout, NamespaceInfo *nspinfo);
--- 163,169 ----
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);
***************
*** 6725,6730 **** dumpType(Archive *fout, TypeInfo *tyinfo)
--- 6726,6733 ----
dumpCompositeType(fout, tyinfo);
else if (tyinfo->typtype == TYPTYPE_ENUM)
dumpEnumType(fout, tyinfo);
+ else if (tyinfo->typtype == TYPTYPE_RANGE)
+ dumpRangeType(fout, tyinfo);
}
/*
***************
*** 6845,6850 **** dumpEnumType(Archive *fout, TypeInfo *tyinfo)
--- 6848,6936 ----
}
/*
+ * 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 201101251
#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
***************
*** 289,294 **** DECLARE_UNIQUE_INDEX(pg_db_role_setting_databaseid_rol_index, 2965, on pg_db_rol
--- 289,299 ----
DECLARE_UNIQUE_INDEX(pg_seclabel_object_index, 3597, on pg_seclabel using btree(objoid oid_ops, classoid oid_ops, objsubid int4_ops, provider text_ops));
#define SecLabelObjectIndexId 3597
+ 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,726 ----
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 ));
+
#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,342 ----
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 ));
#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,219 ----
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 ));
#endif /* PG_OPCLASS_H */
*** a/src/include/catalog/pg_operator.h
--- b/src/include/catalog/pg_operator.h
***************
*** 944,949 **** DATA(insert OID = 2991 ( ">" PGNSP PGUID b f f 2249 2249 16 2990 2992 record
--- 944,971 ----
DATA(insert OID = 2992 ( "<=" PGNSP PGUID b f f 2249 2249 16 2993 2991 record_le scalarltsel scalarltjoinsel ));
DATA(insert OID = 2993 ( ">=" PGNSP PGUID b f f 2249 2249 16 2992 2990 record_ge scalargtsel scalargtjoinsel ));
+ /* generic range type operators */
+ DATA(insert OID = 3882 ( "=" PGNSP PGUID b t t 3831 3831 16 3882 3883 range_eq eqsel eqjoinsel ));
+ DATA(insert OID = 3883 ( "<>" PGNSP PGUID b f f 3831 3831 16 3883 3882 range_ne neqsel neqjoinsel ));
+ DATA(insert OID = 3884 ( "<" PGNSP PGUID b f f 3831 3831 16 3887 3886 range_lt eqsel eqjoinsel ));
+ DATA(insert OID = 3885 ( "<=" PGNSP PGUID b f f 3831 3831 16 3886 3887 range_le eqsel eqjoinsel ));
+ DATA(insert OID = 3886 ( ">=" PGNSP PGUID b f f 3831 3831 16 3885 3884 range_ge eqsel eqjoinsel ));
+ DATA(insert OID = 3887 ( ">" PGNSP PGUID b f f 3831 3831 16 3884 3885 range_gt eqsel eqjoinsel ));
+ DATA(insert OID = 3888 ( "&&" PGNSP PGUID b f f 3831 3831 16 3888 0 3857 scalarltsel scalarltjoinsel ));
+ DATA(insert OID = 3889 ( "@>" PGNSP PGUID b f f 3831 2776 16 3891 0 3858 scalargtsel scalargtjoinsel ));
+ DATA(insert OID = 3890 ( "@>" PGNSP PGUID b f f 3831 3831 16 3892 0 3859 scalargtsel scalargtjoinsel ));
+ DATA(insert OID = 3891 ( "<@" PGNSP PGUID b f f 2776 3831 16 3889 0 3860 scalargtsel scalargtjoinsel ));
+ DATA(insert OID = 3892 ( "<@" PGNSP PGUID b f f 3831 3831 16 3890 0 3861 scalargtsel scalargtjoinsel ));
+ DATA(insert OID = 3893 ( "<<" PGNSP PGUID b f f 3831 3831 16 0 0 before scalargtsel scalargtjoinsel ));
+ DATA(insert OID = 3894 ( ">>" PGNSP PGUID b f f 3831 3831 16 0 0 after scalargtsel scalargtjoinsel ));
+ DATA(insert OID = 3895 ( "&<" PGNSP PGUID b f f 3831 3831 16 0 0 overleft scalarltsel scalarltjoinsel ));
+ DATA(insert OID = 3896 ( "&>" PGNSP PGUID b f f 3831 3831 16 0 0 overright scalargtsel scalargtjoinsel ));
+ DATA(insert OID = 3897 ( "-|-" PGNSP PGUID b f f 3831 3831 16 3897 0 adjacent scalargtsel scalargtjoinsel ));
+ DATA(insert OID = 3898 ( "+" PGNSP PGUID b f f 3831 3831 3831 3898 0 range_union - - ));
+ DATA(insert OID = 3899 ( "-" PGNSP PGUID b f f 3831 3831 3831 0 0 minus - - ));
+ DATA(insert OID = 3900 ( "*" PGNSP PGUID b f f 3831 3831 3831 3900 0 range_intersect - - ));
+ DATA(insert OID = 3920 ( "!?" PGNSP PGUID r f f 3831 0 16 0 3921 3850 - - ));
+ DATA(insert OID = 3921 ( "?" PGNSP PGUID r f f 3831 0 16 0 3920 3829 - - ));
/*
* 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,145 ----
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 ));
#endif /* PG_OPFAMILY_H */
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 1528,1533 **** DATA(insert OID = 1180 ( abstime PGNSP PGUID 12 1 0 0 f f f t f i 1 0 702 "
--- 1528,1539 ----
DESCR("convert timestamp with time zone to abstime");
DATA(insert OID = 1181 ( age PGNSP PGUID 12 1 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 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 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 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 f f f t f i 2 0 1186 "1184 1184" _null_ _null_ _null_ _null_ timestamp_mi _null_ _null_ _null_ ));
DESCR("subtract");
***************
*** 4895,4900 **** DESCR("fetch the last row value");
--- 4901,5019 ----
DATA(insert OID = 3114 ( nth_value PGNSP PGUID 12 1 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 f f f t f i 1 0 16 "3831" _null_ _null_ _null_ _null_ range_empty _null_ _null_ _null_ ));
+ DESCR("is the range empty?");
+ DATA(insert OID = 3829 ( non_empty PGNSP PGUID 12 1 0 0 f f f t f i 1 0 16 "3831" _null_ _null_ _null_ _null_ range_non_empty _null_ _null_ _null_ ));
+ DESCR("is the range non-empty?");
+ DATA(insert OID = 3851 ( lower_inc PGNSP PGUID 12 1 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 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 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 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 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_eq _null_ _null_ _null_ ));
+ DESCR("are the two ranges equal?");
+ DATA(insert OID = 3856 ( range_ne PGNSP PGUID 12 1 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_ne _null_ _null_ _null_ ));
+ DESCR("are the two ranges not equal?");
+ DATA(insert OID = 3857 ( overlaps PGNSP PGUID 12 1 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_overlaps _null_ _null_ _null_ ));
+ DESCR("do the two ranges overlap?");
+ DATA(insert OID = 3858 ( contains PGNSP PGUID 12 1 0 0 f f f t f i 2 0 16 "3831 2776" _null_ _null_ _null_ _null_ range_contains_elem _null_ _null_ _null_ ));
+ DESCR("does the range contain the point?");
+ DATA(insert OID = 3859 ( contains PGNSP PGUID 12 1 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_contains _null_ _null_ _null_ ));
+ DESCR("does the range contain the other range?");
+ DATA(insert OID = 3860 ( contained_by PGNSP PGUID 12 1 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("is the point contained by the range?");
+ DATA(insert OID = 3861 ( contained_by PGNSP PGUID 12 1 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_contained_by _null_ _null_ _null_ ));
+ DESCR("is the range contained by the other range?");
+ DATA(insert OID = 3862 ( adjacent PGNSP PGUID 12 1 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_adjacent _null_ _null_ _null_ ));
+ DESCR("are the two ranges adjacent?");
+ DATA(insert OID = 3863 ( before PGNSP PGUID 12 1 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_before _null_ _null_ _null_ ));
+ DESCR("is the range before the other range?");
+ DATA(insert OID = 3864 ( after PGNSP PGUID 12 1 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_after _null_ _null_ _null_ ));
+ DESCR("is the range after the other range?");
+ DATA(insert OID = 3865 ( overleft PGNSP PGUID 12 1 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_overleft _null_ _null_ _null_ ));
+ DESCR("is the range after the other range?");
+ DATA(insert OID = 3866 ( overright PGNSP PGUID 12 1 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_overright _null_ _null_ _null_ ));
+ DESCR("is the range after the other range?");
+ DATA(insert OID = 3867 ( range_union PGNSP PGUID 12 1 0 0 f f f t f i 2 0 3831 "3831 3831" _null_ _null_ _null_ _null_ range_union _null_ _null_ _null_ ));
+ DESCR("the union of the two ranges");
+ DATA(insert OID = 3868 ( range_intersect PGNSP PGUID 12 1 0 0 f f f t f i 2 0 3831 "3831 3831" _null_ _null_ _null_ _null_ range_intersect _null_ _null_ _null_ ));
+ DESCR("the intersection of the two ranges");
+ DATA(insert OID = 3869 ( minus PGNSP PGUID 12 1 0 0 f f f t f i 2 0 3831 "3831 3831" _null_ _null_ _null_ _null_ range_minus _null_ _null_ _null_ ));
+ DESCR("the set difference of the two ranges");
+ DATA(insert OID = 3870 ( range_cmp PGNSP PGUID 12 1 0 0 f f f t f i 2 0 23 "3831 3831" _null_ _null_ _null_ _null_ range_cmp _null_ _null_ _null_ ));
+ DATA(insert OID = 3871 ( range_lt PGNSP PGUID 12 1 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 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 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 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 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 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 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 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 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 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 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 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 ( intrange_canonical PGNSP PGUID 12 1 0 0 f f f t f i 1 0 3904 "3904" _null_ _null_ _null_ _null_ intrange_canonical _null_ _null_ _null_ ));
+ DESCR("convert an integer range to canonical form");
+ DATA(insert OID = 3915 ( daterange_canonical PGNSP PGUID 12 1 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 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 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 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 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,79 ----
+ /*-------------------------------------------------------------------------
+ *
+ * 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 */
+ regproc rngsubcmp; /* compare values of type 'subtype' */
+ 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 5
+ #define Anum_pg_range_rngtypid 1
+ #define Anum_pg_range_rngsubtype 2
+ #define Anum_pg_range_rngsubcmp 3
+ #define Anum_pg_range_rngcanonical 4
+ #define Anum_pg_range_rngsubfloat 5
+
+ /*
+ * prototypes for functions in pg_range.c
+ */
+
+ void RangeCreate(Oid rangeTypeOid, Oid rangeSubType,
+ regproc rangeSubtypeCmp, regproc rangeCanonical,
+ regproc rangeSubFloat);
+ void RangeDelete(Oid rangeTypeOid);
+
+ /* ----------------
+ * initial contents of pg_range
+ * ----------------
+ */
+ DATA(insert ( 3904 23 btint4cmp intrange_canonical 316 ));
+ DATA(insert ( 3906 1700 numeric_cmp - 1746 ));
+ DATA(insert ( 3908 1114 timestamp_cmp - 3916 ));
+ DATA(insert ( 3910 1184 timestamp_cmp - 3917 ));
+ DATA(insert ( 3912 1082 date_cmp daterange_canonical 3918 ));
+
+ #endif /* PG_RANGE_H */
*** a/src/include/catalog/pg_type.h
--- b/src/include/catalog/pg_type.h
***************
*** 623,629 **** DATA(insert OID = 2776 ( anynonarray PGNSP PGUID 4 t p P f t \054 0 0 0 anynona
#define ANYNONARRAYOID 2776
DATA(insert OID = 3500 ( anyenum PGNSP PGUID 4 t p P f t \054 0 0 0 anyenum_in anyenum_out - - - - - i p f 0 -1 0 _null_ _null_ ));
#define ANYENUMOID 3500
!
/*
* macros
--- 623,654 ----
#define ANYNONARRAYOID 2776
DATA(insert OID = 3500 ( anyenum PGNSP PGUID 4 t p P f t \054 0 0 0 anyenum_in anyenum_out - - - - - i p f 0 -1 0 _null_ _null_ ));
#define ANYENUMOID 3500
! 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 _null_ _null_ ));
! #define ANYRANGEOID 3831
!
!
!
!
! DATA(insert OID = 3904 ( intrange 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 _null_ _null_ ));
! #define INTRANGEOID 3904
! DATA(insert OID = 3905 ( _intrange 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 _null_ _null_ ));
! #define INTRANGEARRAYOID 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 _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 _null_ _null_ ));
! #define NUMRANGEARRAYOID 3907
! DATA(insert OID = 3908 ( period 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 _null_ _null_ ));
! #define PERIODOID 3908
! DATA(insert OID = 3909 ( _period 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 _null_ _null_ ));
! #define PERIODARRAYOID 3909
! DATA(insert OID = 3910 ( periodtz 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 _null_ _null_ ));
! #define PERIODTZOID 3910
! DATA(insert OID = 3911 ( _periodtz 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 _null_ _null_ ));
! #define PERIODTZARRAYOID 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 _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 _null_ _null_ ));
! #define DATERANGEARRAYOID 3913
/*
* macros
***************
*** 633,638 **** DATA(insert OID = 3500 ( anyenum PGNSP PGUID 4 t p P f t \054 0 0 0 anyenum_in
--- 658,664 ----
#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'
***************
*** 644,649 **** DATA(insert OID = 3500 ( anyenum PGNSP PGUID 4 t p P f t \054 0 0 0 anyenum_in
--- 670,676 ----
#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'
***************
*** 655,660 **** DATA(insert OID = 3500 ( anyenum PGNSP PGUID 4 t p P f t \054 0 0 0 anyenum_in
((typid) == ANYELEMENTOID || \
(typid) == ANYARRAYOID || \
(typid) == ANYNONARRAYOID || \
! (typid) == ANYENUMOID)
#endif /* PG_TYPE_H */
--- 682,688 ----
((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
***************
*** 339,344 **** typedef enum NodeTag
--- 339,345 ----
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
***************
*** 2242,2247 **** typedef struct AlterEnumStmt
--- 2242,2258 ----
} 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
***************
*** 92,97 **** extern Oid get_rel_namespace(Oid relid);
--- 92,98 ----
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);
***************
*** 112,117 **** extern Node *get_typdefault(Oid typid);
--- 113,119 ----
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);
***************
*** 138,143 **** extern void free_attstatsslot(Oid atttype,
--- 140,150 ----
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_from_subtype(Oid subtypeOid);
+ extern Oid get_range_subtype_cmp(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,125 ----
+ /*-------------------------------------------------------------------------
+ *
+ * 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 intrange_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
***************
*** 68,73 **** enum SysCacheIdentifier
--- 68,75 ----
OPFAMILYOID,
PROCNAMEARGSNSP,
PROCOID,
+ RANGETYPE,
+ RANGESUBTYPE,
RELNAMENSP,
RELOID,
RULERELNAME,
*** a/src/include/utils/timestamp.h
--- b/src/include/utils/timestamp.h
***************
*** 266,271 **** extern Datum interval_part(PG_FUNCTION_ARGS);
--- 266,272 ----
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);
*** /dev/null
--- b/src/test/regress/expected/rangetypes.out
***************
*** 0 ****
--- 1,424 ----
+ 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, 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 intrange
+ 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")'::period);
+ length
+ ------------------
+ @ 4 days 2 hours
+ (1 row)
+
+ select length('["2000-01-01 01:00:00", "2000-01-01 03:00:00")'::periodtz);
+ length
+ -----------
+ @ 2 hours
+ (1 row)
+
+ select length('["2000-01-01", "2000-01-05")'::daterange);
+ length
+ --------
+ 4
+ (1 row)
+
*** a/src/test/regress/expected/sanity_check.out
--- b/src/test/regress/expected/sanity_check.out
***************
*** 114,119 **** SELECT relname, relhasindex
--- 114,120 ----
pg_opfamily | t
pg_pltemplate | t
pg_proc | t
+ pg_range | t
pg_rewrite | t
pg_seclabel | t
pg_shdepend | t
***************
*** 155,161 **** SELECT relname, relhasindex
timetz_tbl | f
tinterval_tbl | f
varchar_tbl | f
! (144 rows)
--
-- another sanity check: every system catalog that has OIDs should have
--- 156,162 ----
timetz_tbl | f
tinterval_tbl | f
varchar_tbl | f
! (145 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,120 ----
+
+ 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 intrange
+ 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")'::period);
+ select length('["2000-01-01 01:00:00", "2000-01-01 03:00:00")'::periodtz);
+ select length('["2000-01-01", "2000-01-05")'::daterange);
*** 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'));