*** 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'));