1 /*-------------------------------------------------------------------------
4 * I/O functions, operators, and support functions for multirange types.
6 * The stored (serialized) format of a multirange value is:
8 * 12 bytes: MultirangeType struct including varlena header, multirange
9 * type's OID and the number of ranges in the multirange.
10 * 4 * (rangesCount - 1) bytes: 32-bit items pointing to the each range
11 * in the multirange starting from
13 * 1 * rangesCount bytes : 8-bit flags for each range in the multirange
14 * The rest of the multirange are range bound values pointed by multirange
17 * Majority of items contain lengths of corresponding range bound values.
18 * Thanks to that items are typically low numbers. This makes multiranges
19 * compression-friendly. Every MULTIRANGE_ITEM_OFFSET_STRIDE item contains
20 * an offset of the corresponding range bound values. That allows fast lookups
21 * for a particular range index. Offsets are counted starting from the end of
22 * flags aligned to the bound type.
24 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
25 * Portions Copyright (c) 1994, Regents of the University of California
29 * src/backend/utils/adt/multirangetypes.c
31 *-------------------------------------------------------------------------
35 #include "access/tupmacs.h"
36 #include "common/hashfn.h"
38 #include "lib/stringinfo.h"
39 #include "libpq/pqformat.h"
40 #include "nodes/nodes.h"
41 #include "port/pg_bitutils.h"
42 #include "utils/array.h"
43 #include "utils/builtins.h"
44 #include "utils/lsyscache.h"
45 #include "utils/multirangetypes.h"
46 #include "utils/rangetypes.h"
48 /* fn_extra cache entry for one of the range I/O functions */
49 typedef struct MultirangeIOData
51 TypeCacheEntry
*typcache
; /* multirange type's typcache entry */
52 FmgrInfo typioproc
; /* range type's I/O proc */
53 Oid typioparam
; /* range type's I/O parameter */
58 MULTIRANGE_BEFORE_RANGE
,
60 MULTIRANGE_IN_RANGE_ESCAPED
,
61 MULTIRANGE_IN_RANGE_QUOTED
,
62 MULTIRANGE_IN_RANGE_QUOTED_ESCAPED
,
63 MULTIRANGE_AFTER_RANGE
,
65 } MultirangeParseState
;
68 * Macros for accessing past MultirangeType parts of multirange: items, flags
71 #define MultirangeGetItemsPtr(mr) ((uint32 *) ((Pointer) (mr) + \
72 sizeof(MultirangeType)))
73 #define MultirangeGetFlagsPtr(mr) ((uint8 *) ((Pointer) (mr) + \
74 sizeof(MultirangeType) + ((mr)->rangeCount - 1) * sizeof(uint32)))
75 #define MultirangeGetBoundariesPtr(mr, align) ((Pointer) (mr) + \
76 att_align_nominal(sizeof(MultirangeType) + \
77 ((mr)->rangeCount - 1) * sizeof(uint32) + \
78 (mr)->rangeCount * sizeof(uint8), (align)))
80 #define MULTIRANGE_ITEM_OFF_BIT 0x80000000
81 #define MULTIRANGE_ITEM_GET_OFFLEN(item) ((item) & 0x7FFFFFFF)
82 #define MULTIRANGE_ITEM_HAS_OFF(item) ((item) & MULTIRANGE_ITEM_OFF_BIT)
83 #define MULTIRANGE_ITEM_OFFSET_STRIDE 4
85 typedef int (*multirange_bsearch_comparison
) (TypeCacheEntry
*typcache
,
91 static MultirangeIOData
*get_multirange_io_data(FunctionCallInfo fcinfo
,
94 static int32
multirange_canonicalize(TypeCacheEntry
*rangetyp
,
95 int32 input_range_count
,
99 *----------------------------------------------------------
101 *----------------------------------------------------------
105 * Converts string to multirange.
107 * We expect curly brackets to bound the list, with zero or more ranges
108 * separated by commas. We accept whitespace anywhere: before/after our
109 * brackets and around the commas. Ranges can be the empty literal or some
110 * stuff inside parens/brackets. Mostly we delegate parsing the individual
111 * range contents to range_in, but we have to detect quoting and
112 * backslash-escaping which can happen for range bounds. Backslashes can
113 * escape something inside or outside a quoted string, and a quoted string
114 * can escape quote marks with either backslashes or double double-quotes.
117 multirange_in(PG_FUNCTION_ARGS
)
119 char *input_str
= PG_GETARG_CSTRING(0);
120 Oid mltrngtypoid
= PG_GETARG_OID(1);
121 Oid typmod
= PG_GETARG_INT32(2);
122 Node
*escontext
= fcinfo
->context
;
123 TypeCacheEntry
*rangetyp
;
124 int32 ranges_seen
= 0;
125 int32 range_count
= 0;
126 int32 range_capacity
= 8;
128 RangeType
**ranges
= palloc(range_capacity
* sizeof(RangeType
*));
129 MultirangeIOData
*cache
;
131 MultirangeParseState parse_state
;
132 const char *ptr
= input_str
;
133 const char *range_str_begin
= NULL
;
138 cache
= get_multirange_io_data(fcinfo
, mltrngtypoid
, IOFunc_input
);
139 rangetyp
= cache
->typcache
->rngtype
;
141 /* consume whitespace */
142 while (*ptr
!= '\0' && isspace((unsigned char) *ptr
))
148 ereturn(escontext
, (Datum
) 0,
149 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION
),
150 errmsg("malformed multirange literal: \"%s\"",
152 errdetail("Missing left brace.")));
155 parse_state
= MULTIRANGE_BEFORE_RANGE
;
156 for (; parse_state
!= MULTIRANGE_FINISHED
; ptr
++)
161 ereturn(escontext
, (Datum
) 0,
162 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION
),
163 errmsg("malformed multirange literal: \"%s\"",
165 errdetail("Unexpected end of input.")));
167 /* skip whitespace */
168 if (isspace((unsigned char) ch
))
173 case MULTIRANGE_BEFORE_RANGE
:
174 if (ch
== '[' || ch
== '(')
176 range_str_begin
= ptr
;
177 parse_state
= MULTIRANGE_IN_RANGE
;
179 else if (ch
== '}' && ranges_seen
== 0)
180 parse_state
= MULTIRANGE_FINISHED
;
181 else if (pg_strncasecmp(ptr
, RANGE_EMPTY_LITERAL
,
182 strlen(RANGE_EMPTY_LITERAL
)) == 0)
185 /* nothing to do with an empty range */
186 ptr
+= strlen(RANGE_EMPTY_LITERAL
) - 1;
187 parse_state
= MULTIRANGE_AFTER_RANGE
;
190 ereturn(escontext
, (Datum
) 0,
191 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION
),
192 errmsg("malformed multirange literal: \"%s\"",
194 errdetail("Expected range start.")));
196 case MULTIRANGE_IN_RANGE
:
197 if (ch
== ']' || ch
== ')')
199 range_str_len
= ptr
- range_str_begin
+ 1;
200 range_str
= pnstrdup(range_str_begin
, range_str_len
);
201 if (range_capacity
== range_count
)
204 ranges
= (RangeType
**)
205 repalloc(ranges
, range_capacity
* sizeof(RangeType
*));
208 if (!InputFunctionCallSafe(&cache
->typioproc
,
215 range
= DatumGetRangeTypeP(range_datum
);
216 if (!RangeIsEmpty(range
))
217 ranges
[range_count
++] = range
;
218 parse_state
= MULTIRANGE_AFTER_RANGE
;
223 parse_state
= MULTIRANGE_IN_RANGE_QUOTED
;
225 parse_state
= MULTIRANGE_IN_RANGE_ESCAPED
;
228 * We will include this character into range_str once we
229 * find the end of the range value.
233 case MULTIRANGE_IN_RANGE_ESCAPED
:
236 * We will include this character into range_str once we find
237 * the end of the range value.
239 parse_state
= MULTIRANGE_IN_RANGE
;
241 case MULTIRANGE_IN_RANGE_QUOTED
:
243 if (*(ptr
+ 1) == '"')
245 /* two quote marks means an escaped quote mark */
249 parse_state
= MULTIRANGE_IN_RANGE
;
251 parse_state
= MULTIRANGE_IN_RANGE_QUOTED_ESCAPED
;
254 * We will include this character into range_str once we find
255 * the end of the range value.
258 case MULTIRANGE_AFTER_RANGE
:
260 parse_state
= MULTIRANGE_BEFORE_RANGE
;
262 parse_state
= MULTIRANGE_FINISHED
;
264 ereturn(escontext
, (Datum
) 0,
265 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION
),
266 errmsg("malformed multirange literal: \"%s\"",
268 errdetail("Expected comma or end of multirange.")));
270 case MULTIRANGE_IN_RANGE_QUOTED_ESCAPED
:
273 * We will include this character into range_str once we find
274 * the end of the range value.
276 parse_state
= MULTIRANGE_IN_RANGE_QUOTED
;
279 elog(ERROR
, "unknown parse state: %d", parse_state
);
283 /* consume whitespace */
284 while (*ptr
!= '\0' && isspace((unsigned char) *ptr
))
288 ereturn(escontext
, (Datum
) 0,
289 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION
),
290 errmsg("malformed multirange literal: \"%s\"",
292 errdetail("Junk after closing right brace.")));
294 ret
= make_multirange(mltrngtypoid
, rangetyp
, range_count
, ranges
);
295 PG_RETURN_MULTIRANGE_P(ret
);
299 multirange_out(PG_FUNCTION_ARGS
)
301 MultirangeType
*multirange
= PG_GETARG_MULTIRANGE_P(0);
302 Oid mltrngtypoid
= MultirangeTypeGetOid(multirange
);
303 MultirangeIOData
*cache
;
311 cache
= get_multirange_io_data(fcinfo
, mltrngtypoid
, IOFunc_output
);
313 initStringInfo(&buf
);
315 appendStringInfoChar(&buf
, '{');
317 multirange_deserialize(cache
->typcache
->rngtype
, multirange
, &range_count
, &ranges
);
318 for (i
= 0; i
< range_count
; i
++)
321 appendStringInfoChar(&buf
, ',');
323 rangeStr
= OutputFunctionCall(&cache
->typioproc
, RangeTypePGetDatum(range
));
324 appendStringInfoString(&buf
, rangeStr
);
327 appendStringInfoChar(&buf
, '}');
329 PG_RETURN_CSTRING(buf
.data
);
333 * Binary representation: First an int32-sized count of ranges, followed by
334 * ranges in their native binary representation.
337 multirange_recv(PG_FUNCTION_ARGS
)
339 StringInfo buf
= (StringInfo
) PG_GETARG_POINTER(0);
340 Oid mltrngtypoid
= PG_GETARG_OID(1);
341 int32 typmod
= PG_GETARG_INT32(2);
342 MultirangeIOData
*cache
;
346 StringInfoData tmpbuf
;
348 cache
= get_multirange_io_data(fcinfo
, mltrngtypoid
, IOFunc_receive
);
350 range_count
= pq_getmsgint(buf
, 4);
351 ranges
= palloc(range_count
* sizeof(RangeType
*));
353 initStringInfo(&tmpbuf
);
354 for (int i
= 0; i
< range_count
; i
++)
356 uint32 range_len
= pq_getmsgint(buf
, 4);
357 const char *range_data
= pq_getmsgbytes(buf
, range_len
);
359 resetStringInfo(&tmpbuf
);
360 appendBinaryStringInfo(&tmpbuf
, range_data
, range_len
);
362 ranges
[i
] = DatumGetRangeTypeP(ReceiveFunctionCall(&cache
->typioproc
,
371 ret
= make_multirange(mltrngtypoid
, cache
->typcache
->rngtype
,
372 range_count
, ranges
);
373 PG_RETURN_MULTIRANGE_P(ret
);
377 multirange_send(PG_FUNCTION_ARGS
)
379 MultirangeType
*multirange
= PG_GETARG_MULTIRANGE_P(0);
380 Oid mltrngtypoid
= MultirangeTypeGetOid(multirange
);
381 StringInfo buf
= makeStringInfo();
384 MultirangeIOData
*cache
;
386 cache
= get_multirange_io_data(fcinfo
, mltrngtypoid
, IOFunc_send
);
388 /* construct output */
389 pq_begintypsend(buf
);
391 pq_sendint32(buf
, multirange
->rangeCount
);
393 multirange_deserialize(cache
->typcache
->rngtype
, multirange
, &range_count
, &ranges
);
394 for (int i
= 0; i
< range_count
; i
++)
398 range
= RangeTypePGetDatum(ranges
[i
]);
399 range
= PointerGetDatum(SendFunctionCall(&cache
->typioproc
, range
));
401 pq_sendint32(buf
, VARSIZE(range
) - VARHDRSZ
);
402 pq_sendbytes(buf
, VARDATA(range
), VARSIZE(range
) - VARHDRSZ
);
405 PG_RETURN_BYTEA_P(pq_endtypsend(buf
));
409 * get_multirange_io_data: get cached information needed for multirange type I/O
411 * The multirange I/O functions need a bit more cached info than other multirange
412 * functions, so they store a MultirangeIOData struct in fn_extra, not just a
413 * pointer to a type cache entry.
415 static MultirangeIOData
*
416 get_multirange_io_data(FunctionCallInfo fcinfo
, Oid mltrngtypid
, IOFuncSelector func
)
418 MultirangeIOData
*cache
= (MultirangeIOData
*) fcinfo
->flinfo
->fn_extra
;
420 if (cache
== NULL
|| cache
->typcache
->type_id
!= mltrngtypid
)
428 cache
= (MultirangeIOData
*) MemoryContextAlloc(fcinfo
->flinfo
->fn_mcxt
,
429 sizeof(MultirangeIOData
));
430 cache
->typcache
= lookup_type_cache(mltrngtypid
, TYPECACHE_MULTIRANGE_INFO
);
431 if (cache
->typcache
->rngtype
== NULL
)
432 elog(ERROR
, "type %u is not a multirange type", mltrngtypid
);
434 /* get_type_io_data does more than we need, but is convenient */
435 get_type_io_data(cache
->typcache
->rngtype
->type_id
,
444 if (!OidIsValid(typiofunc
))
446 /* this could only happen for receive or send */
447 if (func
== IOFunc_receive
)
449 (errcode(ERRCODE_UNDEFINED_FUNCTION
),
450 errmsg("no binary input function available for type %s",
451 format_type_be(cache
->typcache
->rngtype
->type_id
))));
454 (errcode(ERRCODE_UNDEFINED_FUNCTION
),
455 errmsg("no binary output function available for type %s",
456 format_type_be(cache
->typcache
->rngtype
->type_id
))));
458 fmgr_info_cxt(typiofunc
, &cache
->typioproc
,
459 fcinfo
->flinfo
->fn_mcxt
);
461 fcinfo
->flinfo
->fn_extra
= cache
;
468 * Converts a list of arbitrary ranges into a list that is sorted and merged.
469 * Changes the contents of `ranges`.
471 * Returns the number of slots actually used, which may be less than
472 * input_range_count but never more.
474 * We assume that no input ranges are null, but empties are okay.
477 multirange_canonicalize(TypeCacheEntry
*rangetyp
, int32 input_range_count
,
480 RangeType
*lastRange
= NULL
;
481 RangeType
*currentRange
;
483 int32 output_range_count
= 0;
485 /* Sort the ranges so we can find the ones that overlap/meet. */
486 qsort_arg(ranges
, input_range_count
, sizeof(RangeType
*), range_compare
,
489 /* Now merge where possible: */
490 for (i
= 0; i
< input_range_count
; i
++)
492 currentRange
= ranges
[i
];
493 if (RangeIsEmpty(currentRange
))
496 if (lastRange
== NULL
)
498 ranges
[output_range_count
++] = lastRange
= currentRange
;
503 * range_adjacent_internal gives true if *either* A meets B or B meets
504 * A, which is not quite want we want, but we rely on the sorting
505 * above to rule out B meets A ever happening.
507 if (range_adjacent_internal(rangetyp
, lastRange
, currentRange
))
509 /* The two ranges touch (without overlap), so merge them: */
510 ranges
[output_range_count
- 1] = lastRange
=
511 range_union_internal(rangetyp
, lastRange
, currentRange
, false);
513 else if (range_before_internal(rangetyp
, lastRange
, currentRange
))
515 /* There's a gap, so make a new entry: */
516 lastRange
= ranges
[output_range_count
] = currentRange
;
517 output_range_count
++;
521 /* They must overlap, so merge them: */
522 ranges
[output_range_count
- 1] = lastRange
=
523 range_union_internal(rangetyp
, lastRange
, currentRange
, true);
527 return output_range_count
;
531 *----------------------------------------------------------
534 * These functions aren't in pg_proc, but are useful for
535 * defining new generic multirange functions in C.
536 *----------------------------------------------------------
540 * multirange_get_typcache: get cached information about a multirange type
542 * This is for use by multirange-related functions that follow the convention
543 * of using the fn_extra field as a pointer to the type cache entry for
544 * the multirange type. Functions that need to cache more information than
545 * that must fend for themselves.
548 multirange_get_typcache(FunctionCallInfo fcinfo
, Oid mltrngtypid
)
550 TypeCacheEntry
*typcache
= (TypeCacheEntry
*) fcinfo
->flinfo
->fn_extra
;
552 if (typcache
== NULL
||
553 typcache
->type_id
!= mltrngtypid
)
555 typcache
= lookup_type_cache(mltrngtypid
, TYPECACHE_MULTIRANGE_INFO
);
556 if (typcache
->rngtype
== NULL
)
557 elog(ERROR
, "type %u is not a multirange type", mltrngtypid
);
558 fcinfo
->flinfo
->fn_extra
= typcache
;
566 * Estimate size occupied by serialized multirange.
569 multirange_size_estimate(TypeCacheEntry
*rangetyp
, int32 range_count
,
572 char elemalign
= rangetyp
->rngelemtype
->typalign
;
577 * Count space for MultirangeType struct, items and flags.
579 size
= att_align_nominal(sizeof(MultirangeType
) +
580 Max(range_count
- 1, 0) * sizeof(uint32
) +
581 range_count
* sizeof(uint8
), elemalign
);
583 /* Count space for range bounds */
584 for (i
= 0; i
< range_count
; i
++)
585 size
+= att_align_nominal(VARSIZE(ranges
[i
]) -
587 sizeof(char), elemalign
);
593 * Write multirange data into pre-allocated space.
596 write_multirange_data(MultirangeType
*multirange
, TypeCacheEntry
*rangetyp
,
597 int32 range_count
, RangeType
**ranges
)
600 uint32 prev_offset
= 0;
605 char elemalign
= rangetyp
->rngelemtype
->typalign
;
607 items
= MultirangeGetItemsPtr(multirange
);
608 flags
= MultirangeGetFlagsPtr(multirange
);
609 ptr
= begin
= MultirangeGetBoundariesPtr(multirange
, elemalign
);
610 for (i
= 0; i
< range_count
; i
++)
617 * Every range, except the first one, has an item. Every
618 * MULTIRANGE_ITEM_OFFSET_STRIDE item contains an offset, others
621 items
[i
- 1] = ptr
- begin
;
622 if ((i
% MULTIRANGE_ITEM_OFFSET_STRIDE
) != 0)
623 items
[i
- 1] -= prev_offset
;
625 items
[i
- 1] |= MULTIRANGE_ITEM_OFF_BIT
;
626 prev_offset
= ptr
- begin
;
628 flags
[i
] = *((Pointer
) ranges
[i
] + VARSIZE(ranges
[i
]) - sizeof(char));
629 len
= VARSIZE(ranges
[i
]) - sizeof(RangeType
) - sizeof(char);
630 memcpy(ptr
, (Pointer
) (ranges
[i
] + 1), len
);
631 ptr
+= att_align_nominal(len
, elemalign
);
637 * This serializes the multirange from a list of non-null ranges. It also
638 * sorts the ranges and merges any that touch. The ranges should already be
639 * detoasted, and there should be no NULLs. This should be used by most
642 * Note that we may change the `ranges` parameter (the pointers, but not
643 * any already-existing RangeType contents).
646 make_multirange(Oid mltrngtypoid
, TypeCacheEntry
*rangetyp
, int32 range_count
,
649 MultirangeType
*multirange
;
652 /* Sort and merge input ranges. */
653 range_count
= multirange_canonicalize(rangetyp
, range_count
, ranges
);
655 /* Note: zero-fill is required here, just as in heap tuples */
656 size
= multirange_size_estimate(rangetyp
, range_count
, ranges
);
657 multirange
= palloc0(size
);
658 SET_VARSIZE(multirange
, size
);
660 /* Now fill in the datum */
661 multirange
->multirangetypid
= mltrngtypoid
;
662 multirange
->rangeCount
= range_count
;
664 write_multirange_data(multirange
, rangetyp
, range_count
, ranges
);
670 * Get offset of bounds values of the i'th range in the multirange.
673 multirange_get_bounds_offset(const MultirangeType
*multirange
, int32 i
)
675 uint32
*items
= MultirangeGetItemsPtr(multirange
);
679 * Summarize lengths till we meet an offset.
683 offset
+= MULTIRANGE_ITEM_GET_OFFLEN(items
[i
- 1]);
684 if (MULTIRANGE_ITEM_HAS_OFF(items
[i
- 1]))
692 * Fetch the i'th range from the multirange.
695 multirange_get_range(TypeCacheEntry
*rangetyp
,
696 const MultirangeType
*multirange
, int i
)
702 int16 typlen
= rangetyp
->rngelemtype
->typlen
;
703 char typalign
= rangetyp
->rngelemtype
->typalign
;
707 Assert(i
< multirange
->rangeCount
);
709 offset
= multirange_get_bounds_offset(multirange
, i
);
710 flags
= MultirangeGetFlagsPtr(multirange
)[i
];
711 ptr
= begin
= MultirangeGetBoundariesPtr(multirange
, typalign
) + offset
;
714 * Calculate the size of bound values. In principle, we could get offset
715 * of the next range bound values and calculate accordingly. But range
716 * bound values are aligned, so we have to walk the values to get the
719 if (RANGE_HAS_LBOUND(flags
))
720 ptr
= (Pointer
) att_addlength_pointer(ptr
, typlen
, ptr
);
721 if (RANGE_HAS_UBOUND(flags
))
723 ptr
= (Pointer
) att_align_pointer(ptr
, typalign
, typlen
, ptr
);
724 ptr
= (Pointer
) att_addlength_pointer(ptr
, typlen
, ptr
);
726 len
= (ptr
- begin
) + sizeof(RangeType
) + sizeof(uint8
);
728 range
= palloc0(len
);
729 SET_VARSIZE(range
, len
);
730 range
->rangetypid
= rangetyp
->type_id
;
732 memcpy(range
+ 1, begin
, ptr
- begin
);
733 *((uint8
*) (range
+ 1) + (ptr
- begin
)) = flags
;
739 * Fetch bounds from the i'th range of the multirange. This is the shortcut for
740 * doing the same thing as multirange_get_range() + range_deserialize(), but
741 * performing fewer operations.
744 multirange_get_bounds(TypeCacheEntry
*rangetyp
,
745 const MultirangeType
*multirange
,
746 uint32 i
, RangeBound
*lower
, RangeBound
*upper
)
751 int16 typlen
= rangetyp
->rngelemtype
->typlen
;
752 char typalign
= rangetyp
->rngelemtype
->typalign
;
753 bool typbyval
= rangetyp
->rngelemtype
->typbyval
;
757 Assert(i
< multirange
->rangeCount
);
759 offset
= multirange_get_bounds_offset(multirange
, i
);
760 flags
= MultirangeGetFlagsPtr(multirange
)[i
];
761 ptr
= MultirangeGetBoundariesPtr(multirange
, typalign
) + offset
;
763 /* multirange can't contain empty ranges */
764 Assert((flags
& RANGE_EMPTY
) == 0);
766 /* fetch lower bound, if any */
767 if (RANGE_HAS_LBOUND(flags
))
769 /* att_align_pointer cannot be necessary here */
770 lbound
= fetch_att(ptr
, typbyval
, typlen
);
771 ptr
= (Pointer
) att_addlength_pointer(ptr
, typlen
, ptr
);
776 /* fetch upper bound, if any */
777 if (RANGE_HAS_UBOUND(flags
))
779 ptr
= (Pointer
) att_align_pointer(ptr
, typalign
, typlen
, ptr
);
780 ubound
= fetch_att(ptr
, typbyval
, typlen
);
781 /* no need for att_addlength_pointer */
788 lower
->infinite
= (flags
& RANGE_LB_INF
) != 0;
789 lower
->inclusive
= (flags
& RANGE_LB_INC
) != 0;
793 upper
->infinite
= (flags
& RANGE_UB_INF
) != 0;
794 upper
->inclusive
= (flags
& RANGE_UB_INC
) != 0;
795 upper
->lower
= false;
799 * Construct union range from the multirange.
802 multirange_get_union_range(TypeCacheEntry
*rangetyp
,
803 const MultirangeType
*mr
)
809 if (MultirangeIsEmpty(mr
))
810 return make_empty_range(rangetyp
);
812 multirange_get_bounds(rangetyp
, mr
, 0, &lower
, &tmp
);
813 multirange_get_bounds(rangetyp
, mr
, mr
->rangeCount
- 1, &tmp
, &upper
);
815 return make_range(rangetyp
, &lower
, &upper
, false, NULL
);
820 * multirange_deserialize: deconstruct a multirange value
822 * NB: the given multirange object must be fully detoasted; it cannot have a
823 * short varlena header.
826 multirange_deserialize(TypeCacheEntry
*rangetyp
,
827 const MultirangeType
*multirange
, int32
*range_count
,
830 *range_count
= multirange
->rangeCount
;
832 /* Convert each ShortRangeType into a RangeType */
833 if (*range_count
> 0)
837 *ranges
= palloc(*range_count
* sizeof(RangeType
*));
838 for (i
= 0; i
< *range_count
; i
++)
839 (*ranges
)[i
] = multirange_get_range(rangetyp
, multirange
, i
);
848 make_empty_multirange(Oid mltrngtypoid
, TypeCacheEntry
*rangetyp
)
850 return make_multirange(mltrngtypoid
, rangetyp
, 0, NULL
);
854 * Similar to range_overlaps_internal(), but takes range bounds instead of
855 * ranges as arguments.
858 range_bounds_overlaps(TypeCacheEntry
*typcache
,
859 RangeBound
*lower1
, RangeBound
*upper1
,
860 RangeBound
*lower2
, RangeBound
*upper2
)
862 if (range_cmp_bounds(typcache
, lower1
, lower2
) >= 0 &&
863 range_cmp_bounds(typcache
, lower1
, upper2
) <= 0)
866 if (range_cmp_bounds(typcache
, lower2
, lower1
) >= 0 &&
867 range_cmp_bounds(typcache
, lower2
, upper1
) <= 0)
874 * Similar to range_contains_internal(), but takes range bounds instead of
875 * ranges as arguments.
878 range_bounds_contains(TypeCacheEntry
*typcache
,
879 RangeBound
*lower1
, RangeBound
*upper1
,
880 RangeBound
*lower2
, RangeBound
*upper2
)
882 if (range_cmp_bounds(typcache
, lower1
, lower2
) <= 0 &&
883 range_cmp_bounds(typcache
, upper1
, upper2
) >= 0)
890 * Check if the given key matches any range in multirange using binary search.
891 * If the required range isn't found, that counts as a mismatch. When the
892 * required range is found, the comparison function can still report this as
893 * either match or mismatch. For instance, if we search for containment, we can
894 * found a range, which is overlapping but not containing the key range, and
895 * that would count as a mismatch.
898 multirange_bsearch_match(TypeCacheEntry
*typcache
, const MultirangeType
*mr
,
899 void *key
, multirange_bsearch_comparison cmp_func
)
915 multirange_get_bounds(typcache
, mr
, idx
, &lower
, &upper
);
916 comparison
= (*cmp_func
) (typcache
, &lower
, &upper
, key
, &match
);
920 else if (comparison
> 0)
930 *----------------------------------------------------------
932 *----------------------------------------------------------
936 * Construct multirange value from zero or more ranges. Since this is a
937 * variadic function we get passed an array. The array must contain ranges
938 * that match our return value, and there must be no NULLs.
941 multirange_constructor2(PG_FUNCTION_ARGS
)
943 Oid mltrngtypid
= get_fn_expr_rettype(fcinfo
->flinfo
);
945 TypeCacheEntry
*typcache
;
946 TypeCacheEntry
*rangetyp
;
947 ArrayType
*rangeArray
;
955 typcache
= multirange_get_typcache(fcinfo
, mltrngtypid
);
956 rangetyp
= typcache
->rngtype
;
959 * A no-arg invocation should call multirange_constructor0 instead, but
960 * returning an empty range is what that does.
964 PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid
, rangetyp
, 0, NULL
));
967 * This check should be guaranteed by our signature, but let's do it just
973 "multirange values cannot contain null members");
975 rangeArray
= PG_GETARG_ARRAYTYPE_P(0);
977 dims
= ARR_NDIM(rangeArray
);
980 (errcode(ERRCODE_CARDINALITY_VIOLATION
),
981 errmsg("multiranges cannot be constructed from multidimensional arrays")));
983 rngtypid
= ARR_ELEMTYPE(rangeArray
);
984 if (rngtypid
!= rangetyp
->type_id
)
985 elog(ERROR
, "type %u does not match constructor type", rngtypid
);
988 * Be careful: we can still be called with zero ranges, like this:
989 * `int4multirange(variadic '{}'::int4range[])
998 deconstruct_array(rangeArray
, rngtypid
, rangetyp
->typlen
, rangetyp
->typbyval
,
999 rangetyp
->typalign
, &elements
, &nulls
, &range_count
);
1001 ranges
= palloc0(range_count
* sizeof(RangeType
*));
1002 for (i
= 0; i
< range_count
; i
++)
1006 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED
),
1007 errmsg("multirange values cannot contain null members")));
1009 /* make_multirange will do its own copy */
1010 ranges
[i
] = DatumGetRangeTypeP(elements
[i
]);
1014 PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid
, rangetyp
, range_count
, ranges
));
1018 * Construct multirange value from a single range. It'd be nice if we could
1019 * just use multirange_constructor2 for this case, but we need a non-variadic
1020 * single-arg function to let us define a CAST from a range to its multirange.
1023 multirange_constructor1(PG_FUNCTION_ARGS
)
1025 Oid mltrngtypid
= get_fn_expr_rettype(fcinfo
->flinfo
);
1027 TypeCacheEntry
*typcache
;
1028 TypeCacheEntry
*rangetyp
;
1031 typcache
= multirange_get_typcache(fcinfo
, mltrngtypid
);
1032 rangetyp
= typcache
->rngtype
;
1035 * This check should be guaranteed by our signature, but let's do it just
1039 if (PG_ARGISNULL(0))
1041 "multirange values cannot contain null members");
1043 range
= PG_GETARG_RANGE_P(0);
1045 /* Make sure the range type matches. */
1046 rngtypid
= RangeTypeGetOid(range
);
1047 if (rngtypid
!= rangetyp
->type_id
)
1048 elog(ERROR
, "type %u does not match constructor type", rngtypid
);
1050 PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid
, rangetyp
, 1, &range
));
1054 * Constructor just like multirange_constructor1, but opr_sanity gets angry
1055 * if the same internal function handles multiple functions with different arg
1059 multirange_constructor0(PG_FUNCTION_ARGS
)
1062 TypeCacheEntry
*typcache
;
1063 TypeCacheEntry
*rangetyp
;
1065 /* This should always be called without arguments */
1066 if (PG_NARGS() != 0)
1068 "niladic multirange constructor must not receive arguments");
1070 mltrngtypid
= get_fn_expr_rettype(fcinfo
->flinfo
);
1071 typcache
= multirange_get_typcache(fcinfo
, mltrngtypid
);
1072 rangetyp
= typcache
->rngtype
;
1074 PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid
, rangetyp
, 0, NULL
));
1078 /* multirange, multirange -> multirange type functions */
1080 /* multirange union */
1082 multirange_union(PG_FUNCTION_ARGS
)
1084 MultirangeType
*mr1
= PG_GETARG_MULTIRANGE_P(0);
1085 MultirangeType
*mr2
= PG_GETARG_MULTIRANGE_P(1);
1086 TypeCacheEntry
*typcache
;
1090 RangeType
**ranges1
;
1091 RangeType
**ranges2
;
1092 RangeType
**ranges3
;
1094 if (MultirangeIsEmpty(mr1
))
1095 PG_RETURN_MULTIRANGE_P(mr2
);
1096 if (MultirangeIsEmpty(mr2
))
1097 PG_RETURN_MULTIRANGE_P(mr1
);
1099 typcache
= multirange_get_typcache(fcinfo
, MultirangeTypeGetOid(mr1
));
1101 multirange_deserialize(typcache
->rngtype
, mr1
, &range_count1
, &ranges1
);
1102 multirange_deserialize(typcache
->rngtype
, mr2
, &range_count2
, &ranges2
);
1104 range_count3
= range_count1
+ range_count2
;
1105 ranges3
= palloc0(range_count3
* sizeof(RangeType
*));
1106 memcpy(ranges3
, ranges1
, range_count1
* sizeof(RangeType
*));
1107 memcpy(ranges3
+ range_count1
, ranges2
, range_count2
* sizeof(RangeType
*));
1108 PG_RETURN_MULTIRANGE_P(make_multirange(typcache
->type_id
, typcache
->rngtype
,
1109 range_count3
, ranges3
));
1112 /* multirange minus */
1114 multirange_minus(PG_FUNCTION_ARGS
)
1116 MultirangeType
*mr1
= PG_GETARG_MULTIRANGE_P(0);
1117 MultirangeType
*mr2
= PG_GETARG_MULTIRANGE_P(1);
1118 Oid mltrngtypoid
= MultirangeTypeGetOid(mr1
);
1119 TypeCacheEntry
*typcache
;
1120 TypeCacheEntry
*rangetyp
;
1123 RangeType
**ranges1
;
1124 RangeType
**ranges2
;
1126 typcache
= multirange_get_typcache(fcinfo
, mltrngtypoid
);
1127 rangetyp
= typcache
->rngtype
;
1129 if (MultirangeIsEmpty(mr1
) || MultirangeIsEmpty(mr2
))
1130 PG_RETURN_MULTIRANGE_P(mr1
);
1132 multirange_deserialize(typcache
->rngtype
, mr1
, &range_count1
, &ranges1
);
1133 multirange_deserialize(typcache
->rngtype
, mr2
, &range_count2
, &ranges2
);
1135 PG_RETURN_MULTIRANGE_P(multirange_minus_internal(mltrngtypoid
,
1144 multirange_minus_internal(Oid mltrngtypoid
, TypeCacheEntry
*rangetyp
,
1145 int32 range_count1
, RangeType
**ranges1
,
1146 int32 range_count2
, RangeType
**ranges2
)
1150 RangeType
**ranges3
;
1156 * Worst case: every range in ranges1 makes a different cut to some range
1159 ranges3
= palloc0((range_count1
+ range_count2
) * sizeof(RangeType
*));
1163 * For each range in mr1, keep subtracting until it's gone or the ranges
1164 * in mr2 have passed it. After a subtraction we assign what's left back
1165 * to r1. The parallel progress through mr1 and mr2 is similar to
1166 * multirange_overlaps_multirange_internal.
1169 for (i1
= 0, i2
= 0; i1
< range_count1
; i1
++)
1173 /* Discard r2s while r2 << r1 */
1174 while (r2
!= NULL
&& range_before_internal(rangetyp
, r2
, r1
))
1176 r2
= ++i2
>= range_count2
? NULL
: ranges2
[i2
];
1181 if (range_split_internal(rangetyp
, r1
, r2
, &ranges3
[range_count3
], &r1
))
1184 * If r2 takes a bite out of the middle of r1, we need two
1188 r2
= ++i2
>= range_count2
? NULL
: ranges2
[i2
];
1190 else if (range_overlaps_internal(rangetyp
, r1
, r2
))
1193 * If r2 overlaps r1, replace r1 with r1 - r2.
1195 r1
= range_minus_internal(rangetyp
, r1
, r2
);
1198 * If r2 goes past r1, then we need to stay with it, in case
1199 * it hits future r1s. Otherwise we need to keep r1, in case
1200 * future r2s hit it. Since we already subtracted, there's no
1201 * point in using the overright/overleft calls.
1203 if (RangeIsEmpty(r1
) || range_before_internal(rangetyp
, r1
, r2
))
1206 r2
= ++i2
>= range_count2
? NULL
: ranges2
[i2
];
1211 * This and all future r2s are past r1, so keep them. Also
1212 * assign whatever is left of r1 to the result.
1219 * Nothing else can remove anything from r1, so keep it. Even if r1 is
1220 * empty here, make_multirange will remove it.
1222 ranges3
[range_count3
++] = r1
;
1225 return make_multirange(mltrngtypoid
, rangetyp
, range_count3
, ranges3
);
1228 /* multirange intersection */
1230 multirange_intersect(PG_FUNCTION_ARGS
)
1232 MultirangeType
*mr1
= PG_GETARG_MULTIRANGE_P(0);
1233 MultirangeType
*mr2
= PG_GETARG_MULTIRANGE_P(1);
1234 Oid mltrngtypoid
= MultirangeTypeGetOid(mr1
);
1235 TypeCacheEntry
*typcache
;
1236 TypeCacheEntry
*rangetyp
;
1239 RangeType
**ranges1
;
1240 RangeType
**ranges2
;
1242 typcache
= multirange_get_typcache(fcinfo
, mltrngtypoid
);
1243 rangetyp
= typcache
->rngtype
;
1245 if (MultirangeIsEmpty(mr1
) || MultirangeIsEmpty(mr2
))
1246 PG_RETURN_MULTIRANGE_P(make_empty_multirange(mltrngtypoid
, rangetyp
));
1248 multirange_deserialize(rangetyp
, mr1
, &range_count1
, &ranges1
);
1249 multirange_deserialize(rangetyp
, mr2
, &range_count2
, &ranges2
);
1251 PG_RETURN_MULTIRANGE_P(multirange_intersect_internal(mltrngtypoid
,
1260 multirange_intersect_internal(Oid mltrngtypoid
, TypeCacheEntry
*rangetyp
,
1261 int32 range_count1
, RangeType
**ranges1
,
1262 int32 range_count2
, RangeType
**ranges2
)
1266 RangeType
**ranges3
;
1271 if (range_count1
== 0 || range_count2
== 0)
1272 return make_multirange(mltrngtypoid
, rangetyp
, 0, NULL
);
1274 /*-----------------------------------------------
1275 * Worst case is a stitching pattern like this:
1277 * mr1: --- --- --- ---
1281 * That seems to be range_count1 + range_count2 - 1,
1282 * but one extra won't hurt.
1283 *-----------------------------------------------
1285 ranges3
= palloc0((range_count1
+ range_count2
) * sizeof(RangeType
*));
1289 * For each range in mr1, keep intersecting until the ranges in mr2 have
1290 * passed it. The parallel progress through mr1 and mr2 is similar to
1291 * multirange_minus_multirange_internal, but we don't have to assign back
1295 for (i1
= 0, i2
= 0; i1
< range_count1
; i1
++)
1299 /* Discard r2s while r2 << r1 */
1300 while (r2
!= NULL
&& range_before_internal(rangetyp
, r2
, r1
))
1302 r2
= ++i2
>= range_count2
? NULL
: ranges2
[i2
];
1307 if (range_overlaps_internal(rangetyp
, r1
, r2
))
1309 /* Keep the overlapping part */
1310 ranges3
[range_count3
++] = range_intersect_internal(rangetyp
, r1
, r2
);
1312 /* If we "used up" all of r2, go to the next one... */
1313 if (range_overleft_internal(rangetyp
, r2
, r1
))
1314 r2
= ++i2
>= range_count2
? NULL
: ranges2
[i2
];
1316 /* ...otherwise go to the next r1 */
1321 /* We're past r1, so move to the next one */
1325 /* If we're out of r2s, there can be no more intersections */
1330 return make_multirange(mltrngtypoid
, rangetyp
, range_count3
, ranges3
);
1334 * range_agg_transfn: combine adjacent/overlapping ranges.
1336 * All we do here is gather the input ranges into an array
1337 * so that the finalfn can sort and combine them.
1340 range_agg_transfn(PG_FUNCTION_ARGS
)
1342 MemoryContext aggContext
;
1344 ArrayBuildState
*state
;
1346 if (!AggCheckCallContext(fcinfo
, &aggContext
))
1347 elog(ERROR
, "range_agg_transfn called in non-aggregate context");
1349 rngtypoid
= get_fn_expr_argtype(fcinfo
->flinfo
, 1);
1350 if (!type_is_range(rngtypoid
))
1351 elog(ERROR
, "range_agg must be called with a range");
1353 if (PG_ARGISNULL(0))
1354 state
= initArrayResult(rngtypoid
, aggContext
, false);
1356 state
= (ArrayBuildState
*) PG_GETARG_POINTER(0);
1359 if (!PG_ARGISNULL(1))
1360 accumArrayResult(state
, PG_GETARG_DATUM(1), false, rngtypoid
, aggContext
);
1362 PG_RETURN_POINTER(state
);
1366 * range_agg_finalfn: use our internal array to merge touching ranges.
1368 * Shared by range_agg_finalfn(anyrange) and
1369 * multirange_agg_finalfn(anymultirange).
1372 range_agg_finalfn(PG_FUNCTION_ARGS
)
1374 MemoryContext aggContext
;
1376 TypeCacheEntry
*typcache
;
1377 ArrayBuildState
*state
;
1382 if (!AggCheckCallContext(fcinfo
, &aggContext
))
1383 elog(ERROR
, "range_agg_finalfn called in non-aggregate context");
1385 state
= PG_ARGISNULL(0) ? NULL
: (ArrayBuildState
*) PG_GETARG_POINTER(0);
1387 /* This shouldn't be possible, but just in case.... */
1390 /* Also return NULL if we had zero inputs, like other aggregates */
1391 range_count
= state
->nelems
;
1392 if (range_count
== 0)
1395 mltrngtypoid
= get_fn_expr_rettype(fcinfo
->flinfo
);
1396 typcache
= multirange_get_typcache(fcinfo
, mltrngtypoid
);
1398 ranges
= palloc0(range_count
* sizeof(RangeType
*));
1399 for (i
= 0; i
< range_count
; i
++)
1400 ranges
[i
] = DatumGetRangeTypeP(state
->dvalues
[i
]);
1402 PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypoid
, typcache
->rngtype
, range_count
, ranges
));
1406 * multirange_agg_transfn: combine adjacent/overlapping multiranges.
1408 * All we do here is gather the input multiranges' ranges into an array so
1409 * that the finalfn can sort and combine them.
1412 multirange_agg_transfn(PG_FUNCTION_ARGS
)
1414 MemoryContext aggContext
;
1416 TypeCacheEntry
*typcache
;
1417 TypeCacheEntry
*rngtypcache
;
1418 ArrayBuildState
*state
;
1420 if (!AggCheckCallContext(fcinfo
, &aggContext
))
1421 elog(ERROR
, "multirange_agg_transfn called in non-aggregate context");
1423 mltrngtypoid
= get_fn_expr_argtype(fcinfo
->flinfo
, 1);
1424 if (!type_is_multirange(mltrngtypoid
))
1425 elog(ERROR
, "range_agg must be called with a multirange");
1427 typcache
= multirange_get_typcache(fcinfo
, mltrngtypoid
);
1428 rngtypcache
= typcache
->rngtype
;
1430 if (PG_ARGISNULL(0))
1431 state
= initArrayResult(rngtypcache
->type_id
, aggContext
, false);
1433 state
= (ArrayBuildState
*) PG_GETARG_POINTER(0);
1436 if (!PG_ARGISNULL(1))
1438 MultirangeType
*current
;
1442 current
= PG_GETARG_MULTIRANGE_P(1);
1443 multirange_deserialize(rngtypcache
, current
, &range_count
, &ranges
);
1444 if (range_count
== 0)
1447 * Add an empty range so we get an empty result (not a null
1450 accumArrayResult(state
,
1451 RangeTypePGetDatum(make_empty_range(rngtypcache
)),
1452 false, rngtypcache
->type_id
, aggContext
);
1456 for (int32 i
= 0; i
< range_count
; i
++)
1457 accumArrayResult(state
, RangeTypePGetDatum(ranges
[i
]), false, rngtypcache
->type_id
, aggContext
);
1461 PG_RETURN_POINTER(state
);
1465 multirange_intersect_agg_transfn(PG_FUNCTION_ARGS
)
1467 MemoryContext aggContext
;
1469 TypeCacheEntry
*typcache
;
1470 MultirangeType
*result
;
1471 MultirangeType
*current
;
1474 RangeType
**ranges1
;
1475 RangeType
**ranges2
;
1477 if (!AggCheckCallContext(fcinfo
, &aggContext
))
1478 elog(ERROR
, "multirange_intersect_agg_transfn called in non-aggregate context");
1480 mltrngtypoid
= get_fn_expr_argtype(fcinfo
->flinfo
, 1);
1481 if (!type_is_multirange(mltrngtypoid
))
1482 elog(ERROR
, "range_intersect_agg must be called with a multirange");
1484 typcache
= multirange_get_typcache(fcinfo
, mltrngtypoid
);
1486 /* strictness ensures these are non-null */
1487 result
= PG_GETARG_MULTIRANGE_P(0);
1488 current
= PG_GETARG_MULTIRANGE_P(1);
1490 multirange_deserialize(typcache
->rngtype
, result
, &range_count1
, &ranges1
);
1491 multirange_deserialize(typcache
->rngtype
, current
, &range_count2
, &ranges2
);
1493 result
= multirange_intersect_internal(mltrngtypoid
,
1499 PG_RETURN_MULTIRANGE_P(result
);
1503 /* multirange -> element type functions */
1505 /* extract lower bound value */
1507 multirange_lower(PG_FUNCTION_ARGS
)
1509 MultirangeType
*mr
= PG_GETARG_MULTIRANGE_P(0);
1510 TypeCacheEntry
*typcache
;
1514 if (MultirangeIsEmpty(mr
))
1517 typcache
= multirange_get_typcache(fcinfo
, MultirangeTypeGetOid(mr
));
1519 multirange_get_bounds(typcache
->rngtype
, mr
, 0,
1522 if (!lower
.infinite
)
1523 PG_RETURN_DATUM(lower
.val
);
1528 /* extract upper bound value */
1530 multirange_upper(PG_FUNCTION_ARGS
)
1532 MultirangeType
*mr
= PG_GETARG_MULTIRANGE_P(0);
1533 TypeCacheEntry
*typcache
;
1537 if (MultirangeIsEmpty(mr
))
1540 typcache
= multirange_get_typcache(fcinfo
, MultirangeTypeGetOid(mr
));
1542 multirange_get_bounds(typcache
->rngtype
, mr
, mr
->rangeCount
- 1,
1545 if (!upper
.infinite
)
1546 PG_RETURN_DATUM(upper
.val
);
1552 /* multirange -> bool functions */
1554 /* is multirange empty? */
1556 multirange_empty(PG_FUNCTION_ARGS
)
1558 MultirangeType
*mr
= PG_GETARG_MULTIRANGE_P(0);
1560 PG_RETURN_BOOL(MultirangeIsEmpty(mr
));
1563 /* is lower bound inclusive? */
1565 multirange_lower_inc(PG_FUNCTION_ARGS
)
1567 MultirangeType
*mr
= PG_GETARG_MULTIRANGE_P(0);
1568 TypeCacheEntry
*typcache
;
1572 if (MultirangeIsEmpty(mr
))
1573 PG_RETURN_BOOL(false);
1575 typcache
= multirange_get_typcache(fcinfo
, MultirangeTypeGetOid(mr
));
1576 multirange_get_bounds(typcache
->rngtype
, mr
, 0,
1579 PG_RETURN_BOOL(lower
.inclusive
);
1582 /* is upper bound inclusive? */
1584 multirange_upper_inc(PG_FUNCTION_ARGS
)
1586 MultirangeType
*mr
= PG_GETARG_MULTIRANGE_P(0);
1587 TypeCacheEntry
*typcache
;
1591 if (MultirangeIsEmpty(mr
))
1592 PG_RETURN_BOOL(false);
1594 typcache
= multirange_get_typcache(fcinfo
, MultirangeTypeGetOid(mr
));
1595 multirange_get_bounds(typcache
->rngtype
, mr
, mr
->rangeCount
- 1,
1598 PG_RETURN_BOOL(upper
.inclusive
);
1601 /* is lower bound infinite? */
1603 multirange_lower_inf(PG_FUNCTION_ARGS
)
1605 MultirangeType
*mr
= PG_GETARG_MULTIRANGE_P(0);
1606 TypeCacheEntry
*typcache
;
1610 if (MultirangeIsEmpty(mr
))
1611 PG_RETURN_BOOL(false);
1613 typcache
= multirange_get_typcache(fcinfo
, MultirangeTypeGetOid(mr
));
1614 multirange_get_bounds(typcache
->rngtype
, mr
, 0,
1617 PG_RETURN_BOOL(lower
.infinite
);
1620 /* is upper bound infinite? */
1622 multirange_upper_inf(PG_FUNCTION_ARGS
)
1624 MultirangeType
*mr
= PG_GETARG_MULTIRANGE_P(0);
1625 TypeCacheEntry
*typcache
;
1629 if (MultirangeIsEmpty(mr
))
1630 PG_RETURN_BOOL(false);
1632 typcache
= multirange_get_typcache(fcinfo
, MultirangeTypeGetOid(mr
));
1633 multirange_get_bounds(typcache
->rngtype
, mr
, mr
->rangeCount
- 1,
1636 PG_RETURN_BOOL(upper
.infinite
);
1641 /* multirange, element -> bool functions */
1645 multirange_contains_elem(PG_FUNCTION_ARGS
)
1647 MultirangeType
*mr
= PG_GETARG_MULTIRANGE_P(0);
1648 Datum val
= PG_GETARG_DATUM(1);
1649 TypeCacheEntry
*typcache
;
1651 typcache
= multirange_get_typcache(fcinfo
, MultirangeTypeGetOid(mr
));
1653 PG_RETURN_BOOL(multirange_contains_elem_internal(typcache
->rngtype
, mr
, val
));
1658 elem_contained_by_multirange(PG_FUNCTION_ARGS
)
1660 Datum val
= PG_GETARG_DATUM(0);
1661 MultirangeType
*mr
= PG_GETARG_MULTIRANGE_P(1);
1662 TypeCacheEntry
*typcache
;
1664 typcache
= multirange_get_typcache(fcinfo
, MultirangeTypeGetOid(mr
));
1666 PG_RETURN_BOOL(multirange_contains_elem_internal(typcache
->rngtype
, mr
, val
));
1670 * Comparison function for checking if any range of multirange contains given
1671 * key element using binary search.
1674 multirange_elem_bsearch_comparison(TypeCacheEntry
*typcache
,
1675 RangeBound
*lower
, RangeBound
*upper
,
1676 void *key
, bool *match
)
1678 Datum val
= *((Datum
*) key
);
1681 if (!lower
->infinite
)
1683 cmp
= DatumGetInt32(FunctionCall2Coll(&typcache
->rng_cmp_proc_finfo
,
1684 typcache
->rng_collation
,
1686 if (cmp
> 0 || (cmp
== 0 && !lower
->inclusive
))
1690 if (!upper
->infinite
)
1692 cmp
= DatumGetInt32(FunctionCall2Coll(&typcache
->rng_cmp_proc_finfo
,
1693 typcache
->rng_collation
,
1695 if (cmp
< 0 || (cmp
== 0 && !upper
->inclusive
))
1704 * Test whether multirange mr contains a specific element value.
1707 multirange_contains_elem_internal(TypeCacheEntry
*rangetyp
,
1708 const MultirangeType
*mr
, Datum val
)
1710 if (MultirangeIsEmpty(mr
))
1713 return multirange_bsearch_match(rangetyp
, mr
, &val
,
1714 multirange_elem_bsearch_comparison
);
1717 /* multirange, range -> bool functions */
1721 multirange_contains_range(PG_FUNCTION_ARGS
)
1723 MultirangeType
*mr
= PG_GETARG_MULTIRANGE_P(0);
1724 RangeType
*r
= PG_GETARG_RANGE_P(1);
1725 TypeCacheEntry
*typcache
;
1727 typcache
= multirange_get_typcache(fcinfo
, MultirangeTypeGetOid(mr
));
1729 PG_RETURN_BOOL(multirange_contains_range_internal(typcache
->rngtype
, mr
, r
));
1733 range_contains_multirange(PG_FUNCTION_ARGS
)
1735 RangeType
*r
= PG_GETARG_RANGE_P(0);
1736 MultirangeType
*mr
= PG_GETARG_MULTIRANGE_P(1);
1737 TypeCacheEntry
*typcache
;
1739 typcache
= multirange_get_typcache(fcinfo
, MultirangeTypeGetOid(mr
));
1741 PG_RETURN_BOOL(range_contains_multirange_internal(typcache
->rngtype
, r
, mr
));
1746 range_contained_by_multirange(PG_FUNCTION_ARGS
)
1748 RangeType
*r
= PG_GETARG_RANGE_P(0);
1749 MultirangeType
*mr
= PG_GETARG_MULTIRANGE_P(1);
1750 TypeCacheEntry
*typcache
;
1752 typcache
= multirange_get_typcache(fcinfo
, MultirangeTypeGetOid(mr
));
1754 PG_RETURN_BOOL(multirange_contains_range_internal(typcache
->rngtype
, mr
, r
));
1758 multirange_contained_by_range(PG_FUNCTION_ARGS
)
1760 MultirangeType
*mr
= PG_GETARG_MULTIRANGE_P(0);
1761 RangeType
*r
= PG_GETARG_RANGE_P(1);
1762 TypeCacheEntry
*typcache
;
1764 typcache
= multirange_get_typcache(fcinfo
, MultirangeTypeGetOid(mr
));
1766 PG_RETURN_BOOL(range_contains_multirange_internal(typcache
->rngtype
, r
, mr
));
1770 * Comparison function for checking if any range of multirange contains given
1771 * key range using binary search.
1774 multirange_range_contains_bsearch_comparison(TypeCacheEntry
*typcache
,
1775 RangeBound
*lower
, RangeBound
*upper
,
1776 void *key
, bool *match
)
1778 RangeBound
*keyLower
= (RangeBound
*) key
;
1779 RangeBound
*keyUpper
= (RangeBound
*) key
+ 1;
1781 /* Check if key range is strictly in the left or in the right */
1782 if (range_cmp_bounds(typcache
, keyUpper
, lower
) < 0)
1784 if (range_cmp_bounds(typcache
, keyLower
, upper
) > 0)
1788 * At this point we found overlapping range. But we have to check if it
1789 * really contains the key range. Anyway, we have to stop our search
1790 * here, because multirange contains only non-overlapping ranges.
1792 *match
= range_bounds_contains(typcache
, lower
, upper
, keyLower
, keyUpper
);
1798 * Test whether multirange mr contains a specific range r.
1801 multirange_contains_range_internal(TypeCacheEntry
*rangetyp
,
1802 const MultirangeType
*mr
,
1805 RangeBound bounds
[2];
1809 * Every multirange contains an infinite number of empty ranges, even an
1812 if (RangeIsEmpty(r
))
1815 if (MultirangeIsEmpty(mr
))
1818 range_deserialize(rangetyp
, r
, &bounds
[0], &bounds
[1], &empty
);
1821 return multirange_bsearch_match(rangetyp
, mr
, bounds
,
1822 multirange_range_contains_bsearch_comparison
);
1826 * Test whether range r contains a multirange mr.
1829 range_contains_multirange_internal(TypeCacheEntry
*rangetyp
,
1831 const MultirangeType
*mr
)
1841 * Every range contains an infinite number of empty multiranges, even an
1844 if (MultirangeIsEmpty(mr
))
1847 if (RangeIsEmpty(r
))
1850 /* Range contains multirange iff it contains its union range. */
1851 range_deserialize(rangetyp
, r
, &lower1
, &upper1
, &empty
);
1853 multirange_get_bounds(rangetyp
, mr
, 0, &lower2
, &tmp
);
1854 multirange_get_bounds(rangetyp
, mr
, mr
->rangeCount
- 1, &tmp
, &upper2
);
1856 return range_bounds_contains(rangetyp
, &lower1
, &upper1
, &lower2
, &upper2
);
1860 /* multirange, multirange -> bool functions */
1862 /* equality (internal version) */
1864 multirange_eq_internal(TypeCacheEntry
*rangetyp
,
1865 const MultirangeType
*mr1
,
1866 const MultirangeType
*mr2
)
1868 int32 range_count_1
;
1869 int32 range_count_2
;
1876 /* Different types should be prevented by ANYMULTIRANGE matching rules */
1877 if (MultirangeTypeGetOid(mr1
) != MultirangeTypeGetOid(mr2
))
1878 elog(ERROR
, "multirange types do not match");
1880 range_count_1
= mr1
->rangeCount
;
1881 range_count_2
= mr2
->rangeCount
;
1883 if (range_count_1
!= range_count_2
)
1886 for (i
= 0; i
< range_count_1
; i
++)
1888 multirange_get_bounds(rangetyp
, mr1
, i
, &lower1
, &upper1
);
1889 multirange_get_bounds(rangetyp
, mr2
, i
, &lower2
, &upper2
);
1891 if (range_cmp_bounds(rangetyp
, &lower1
, &lower2
) != 0 ||
1892 range_cmp_bounds(rangetyp
, &upper1
, &upper2
) != 0)
1901 multirange_eq(PG_FUNCTION_ARGS
)
1903 MultirangeType
*mr1
= PG_GETARG_MULTIRANGE_P(0);
1904 MultirangeType
*mr2
= PG_GETARG_MULTIRANGE_P(1);
1905 TypeCacheEntry
*typcache
;
1907 typcache
= multirange_get_typcache(fcinfo
, MultirangeTypeGetOid(mr1
));
1909 PG_RETURN_BOOL(multirange_eq_internal(typcache
->rngtype
, mr1
, mr2
));
1912 /* inequality (internal version) */
1914 multirange_ne_internal(TypeCacheEntry
*rangetyp
,
1915 const MultirangeType
*mr1
,
1916 const MultirangeType
*mr2
)
1918 return (!multirange_eq_internal(rangetyp
, mr1
, mr2
));
1923 multirange_ne(PG_FUNCTION_ARGS
)
1925 MultirangeType
*mr1
= PG_GETARG_MULTIRANGE_P(0);
1926 MultirangeType
*mr2
= PG_GETARG_MULTIRANGE_P(1);
1927 TypeCacheEntry
*typcache
;
1929 typcache
= multirange_get_typcache(fcinfo
, MultirangeTypeGetOid(mr1
));
1931 PG_RETURN_BOOL(multirange_ne_internal(typcache
->rngtype
, mr1
, mr2
));
1936 range_overlaps_multirange(PG_FUNCTION_ARGS
)
1938 RangeType
*r
= PG_GETARG_RANGE_P(0);
1939 MultirangeType
*mr
= PG_GETARG_MULTIRANGE_P(1);
1940 TypeCacheEntry
*typcache
;
1942 typcache
= multirange_get_typcache(fcinfo
, MultirangeTypeGetOid(mr
));
1944 PG_RETURN_BOOL(range_overlaps_multirange_internal(typcache
->rngtype
, r
, mr
));
1948 multirange_overlaps_range(PG_FUNCTION_ARGS
)
1950 MultirangeType
*mr
= PG_GETARG_MULTIRANGE_P(0);
1951 RangeType
*r
= PG_GETARG_RANGE_P(1);
1952 TypeCacheEntry
*typcache
;
1954 typcache
= multirange_get_typcache(fcinfo
, MultirangeTypeGetOid(mr
));
1956 PG_RETURN_BOOL(range_overlaps_multirange_internal(typcache
->rngtype
, r
, mr
));
1960 multirange_overlaps_multirange(PG_FUNCTION_ARGS
)
1962 MultirangeType
*mr1
= PG_GETARG_MULTIRANGE_P(0);
1963 MultirangeType
*mr2
= PG_GETARG_MULTIRANGE_P(1);
1964 TypeCacheEntry
*typcache
;
1966 typcache
= multirange_get_typcache(fcinfo
, MultirangeTypeGetOid(mr1
));
1968 PG_RETURN_BOOL(multirange_overlaps_multirange_internal(typcache
->rngtype
, mr1
, mr2
));
1972 * Comparison function for checking if any range of multirange overlaps given
1973 * key range using binary search.
1976 multirange_range_overlaps_bsearch_comparison(TypeCacheEntry
*typcache
,
1977 RangeBound
*lower
, RangeBound
*upper
,
1978 void *key
, bool *match
)
1980 RangeBound
*keyLower
= (RangeBound
*) key
;
1981 RangeBound
*keyUpper
= (RangeBound
*) key
+ 1;
1983 if (range_cmp_bounds(typcache
, keyUpper
, lower
) < 0)
1985 if (range_cmp_bounds(typcache
, keyLower
, upper
) > 0)
1993 range_overlaps_multirange_internal(TypeCacheEntry
*rangetyp
,
1995 const MultirangeType
*mr
)
1997 RangeBound bounds
[2];
2001 * Empties never overlap, even with empties. (This seems strange since
2002 * they *do* contain each other, but we want to follow how ranges work.)
2004 if (RangeIsEmpty(r
) || MultirangeIsEmpty(mr
))
2007 range_deserialize(rangetyp
, r
, &bounds
[0], &bounds
[1], &empty
);
2010 return multirange_bsearch_match(rangetyp
, mr
, bounds
,
2011 multirange_range_overlaps_bsearch_comparison
);
2015 multirange_overlaps_multirange_internal(TypeCacheEntry
*rangetyp
,
2016 const MultirangeType
*mr1
,
2017 const MultirangeType
*mr2
)
2029 * Empties never overlap, even with empties. (This seems strange since
2030 * they *do* contain each other, but we want to follow how ranges work.)
2032 if (MultirangeIsEmpty(mr1
) || MultirangeIsEmpty(mr2
))
2035 range_count1
= mr1
->rangeCount
;
2036 range_count2
= mr2
->rangeCount
;
2039 * Every range in mr1 gets a chance to overlap with the ranges in mr2, but
2040 * we can use their ordering to avoid O(n^2). This is similar to
2041 * range_overlaps_multirange where r1 : r2 :: mrr : r, but there if we
2042 * don't find an overlap with r we're done, and here if we don't find an
2043 * overlap with r2 we try the next r2.
2046 multirange_get_bounds(rangetyp
, mr1
, i1
, &lower1
, &upper1
);
2047 for (i1
= 0, i2
= 0; i2
< range_count2
; i2
++)
2049 multirange_get_bounds(rangetyp
, mr2
, i2
, &lower2
, &upper2
);
2051 /* Discard r1s while r1 << r2 */
2052 while (range_cmp_bounds(rangetyp
, &upper1
, &lower2
) < 0)
2054 if (++i1
>= range_count1
)
2056 multirange_get_bounds(rangetyp
, mr1
, i1
, &lower1
, &upper1
);
2060 * If r1 && r2, we're done, otherwise we failed to find an overlap for
2061 * r2, so go to the next one.
2063 if (range_bounds_overlaps(rangetyp
, &lower1
, &upper1
, &lower2
, &upper2
))
2067 /* We looked through all of mr2 without finding an overlap */
2071 /* does not extend to right of? */
2073 range_overleft_multirange_internal(TypeCacheEntry
*rangetyp
,
2075 const MultirangeType
*mr
)
2083 if (RangeIsEmpty(r
) || MultirangeIsEmpty(mr
))
2084 PG_RETURN_BOOL(false);
2087 range_deserialize(rangetyp
, r
, &lower1
, &upper1
, &empty
);
2089 multirange_get_bounds(rangetyp
, mr
, mr
->rangeCount
- 1,
2092 PG_RETURN_BOOL(range_cmp_bounds(rangetyp
, &upper1
, &upper2
) <= 0);
2096 range_overleft_multirange(PG_FUNCTION_ARGS
)
2098 RangeType
*r
= PG_GETARG_RANGE_P(0);
2099 MultirangeType
*mr
= PG_GETARG_MULTIRANGE_P(1);
2100 TypeCacheEntry
*typcache
;
2102 typcache
= multirange_get_typcache(fcinfo
, MultirangeTypeGetOid(mr
));
2104 PG_RETURN_BOOL(range_overleft_multirange_internal(typcache
->rngtype
, r
, mr
));
2108 multirange_overleft_range(PG_FUNCTION_ARGS
)
2110 MultirangeType
*mr
= PG_GETARG_MULTIRANGE_P(0);
2111 RangeType
*r
= PG_GETARG_RANGE_P(1);
2112 TypeCacheEntry
*typcache
;
2119 if (MultirangeIsEmpty(mr
) || RangeIsEmpty(r
))
2120 PG_RETURN_BOOL(false);
2122 typcache
= multirange_get_typcache(fcinfo
, MultirangeTypeGetOid(mr
));
2124 multirange_get_bounds(typcache
->rngtype
, mr
, mr
->rangeCount
- 1,
2126 range_deserialize(typcache
->rngtype
, r
, &lower2
, &upper2
, &empty
);
2129 PG_RETURN_BOOL(range_cmp_bounds(typcache
->rngtype
, &upper1
, &upper2
) <= 0);
2133 multirange_overleft_multirange(PG_FUNCTION_ARGS
)
2135 MultirangeType
*mr1
= PG_GETARG_MULTIRANGE_P(0);
2136 MultirangeType
*mr2
= PG_GETARG_MULTIRANGE_P(1);
2137 TypeCacheEntry
*typcache
;
2143 if (MultirangeIsEmpty(mr1
) || MultirangeIsEmpty(mr2
))
2144 PG_RETURN_BOOL(false);
2146 typcache
= multirange_get_typcache(fcinfo
, MultirangeTypeGetOid(mr1
));
2148 multirange_get_bounds(typcache
->rngtype
, mr1
, mr1
->rangeCount
- 1,
2150 multirange_get_bounds(typcache
->rngtype
, mr2
, mr2
->rangeCount
- 1,
2153 PG_RETURN_BOOL(range_cmp_bounds(typcache
->rngtype
, &upper1
, &upper2
) <= 0);
2156 /* does not extend to left of? */
2158 range_overright_multirange_internal(TypeCacheEntry
*rangetyp
,
2160 const MultirangeType
*mr
)
2168 if (RangeIsEmpty(r
) || MultirangeIsEmpty(mr
))
2169 PG_RETURN_BOOL(false);
2171 range_deserialize(rangetyp
, r
, &lower1
, &upper1
, &empty
);
2173 multirange_get_bounds(rangetyp
, mr
, 0, &lower2
, &upper2
);
2175 return (range_cmp_bounds(rangetyp
, &lower1
, &lower2
) >= 0);
2179 range_overright_multirange(PG_FUNCTION_ARGS
)
2181 RangeType
*r
= PG_GETARG_RANGE_P(0);
2182 MultirangeType
*mr
= PG_GETARG_MULTIRANGE_P(1);
2183 TypeCacheEntry
*typcache
;
2185 typcache
= multirange_get_typcache(fcinfo
, MultirangeTypeGetOid(mr
));
2187 PG_RETURN_BOOL(range_overright_multirange_internal(typcache
->rngtype
, r
, mr
));
2191 multirange_overright_range(PG_FUNCTION_ARGS
)
2193 MultirangeType
*mr
= PG_GETARG_MULTIRANGE_P(0);
2194 RangeType
*r
= PG_GETARG_RANGE_P(1);
2195 TypeCacheEntry
*typcache
;
2202 if (MultirangeIsEmpty(mr
) || RangeIsEmpty(r
))
2203 PG_RETURN_BOOL(false);
2205 typcache
= multirange_get_typcache(fcinfo
, MultirangeTypeGetOid(mr
));
2207 multirange_get_bounds(typcache
->rngtype
, mr
, 0, &lower1
, &upper1
);
2208 range_deserialize(typcache
->rngtype
, r
, &lower2
, &upper2
, &empty
);
2211 PG_RETURN_BOOL(range_cmp_bounds(typcache
->rngtype
, &lower1
, &lower2
) >= 0);
2215 multirange_overright_multirange(PG_FUNCTION_ARGS
)
2217 MultirangeType
*mr1
= PG_GETARG_MULTIRANGE_P(0);
2218 MultirangeType
*mr2
= PG_GETARG_MULTIRANGE_P(1);
2219 TypeCacheEntry
*typcache
;
2225 if (MultirangeIsEmpty(mr1
) || MultirangeIsEmpty(mr2
))
2226 PG_RETURN_BOOL(false);
2228 typcache
= multirange_get_typcache(fcinfo
, MultirangeTypeGetOid(mr1
));
2230 multirange_get_bounds(typcache
->rngtype
, mr1
, 0, &lower1
, &upper1
);
2231 multirange_get_bounds(typcache
->rngtype
, mr2
, 0, &lower2
, &upper2
);
2233 PG_RETURN_BOOL(range_cmp_bounds(typcache
->rngtype
, &lower1
, &lower2
) >= 0);
2238 multirange_contains_multirange(PG_FUNCTION_ARGS
)
2240 MultirangeType
*mr1
= PG_GETARG_MULTIRANGE_P(0);
2241 MultirangeType
*mr2
= PG_GETARG_MULTIRANGE_P(1);
2242 TypeCacheEntry
*typcache
;
2244 typcache
= multirange_get_typcache(fcinfo
, MultirangeTypeGetOid(mr1
));
2246 PG_RETURN_BOOL(multirange_contains_multirange_internal(typcache
->rngtype
, mr1
, mr2
));
2251 multirange_contained_by_multirange(PG_FUNCTION_ARGS
)
2253 MultirangeType
*mr1
= PG_GETARG_MULTIRANGE_P(0);
2254 MultirangeType
*mr2
= PG_GETARG_MULTIRANGE_P(1);
2255 TypeCacheEntry
*typcache
;
2257 typcache
= multirange_get_typcache(fcinfo
, MultirangeTypeGetOid(mr1
));
2259 PG_RETURN_BOOL(multirange_contains_multirange_internal(typcache
->rngtype
, mr2
, mr1
));
2263 * Test whether multirange mr1 contains every range from another multirange mr2.
2266 multirange_contains_multirange_internal(TypeCacheEntry
*rangetyp
,
2267 const MultirangeType
*mr1
,
2268 const MultirangeType
*mr2
)
2270 int32 range_count1
= mr1
->rangeCount
;
2271 int32 range_count2
= mr2
->rangeCount
;
2280 * We follow the same logic for empties as ranges: - an empty multirange
2281 * contains an empty range/multirange. - an empty multirange can't contain
2282 * any other range/multirange. - an empty multirange is contained by any
2283 * other range/multirange.
2286 if (range_count2
== 0)
2288 if (range_count1
== 0)
2292 * Every range in mr2 must be contained by some range in mr1. To avoid
2293 * O(n^2) we walk through both ranges in tandem.
2296 multirange_get_bounds(rangetyp
, mr1
, i1
, &lower1
, &upper1
);
2297 for (i2
= 0; i2
< range_count2
; i2
++)
2299 multirange_get_bounds(rangetyp
, mr2
, i2
, &lower2
, &upper2
);
2301 /* Discard r1s while r1 << r2 */
2302 while (range_cmp_bounds(rangetyp
, &upper1
, &lower2
) < 0)
2304 if (++i1
>= range_count1
)
2306 multirange_get_bounds(rangetyp
, mr1
, i1
, &lower1
, &upper1
);
2310 * If r1 @> r2, go to the next r2, otherwise return false (since every
2311 * r1[n] and r1[n+1] must have a gap). Note this will give weird
2312 * answers if you don't canonicalize, e.g. with a custom
2313 * int2multirange {[1,1], [2,2]} there is a "gap". But that is
2314 * consistent with other range operators, e.g. '[1,1]'::int2range -|-
2315 * '[2,2]'::int2range is false.
2317 if (!range_bounds_contains(rangetyp
, &lower1
, &upper1
,
2322 /* All ranges in mr2 are satisfied */
2326 /* strictly left of? */
2328 range_before_multirange(PG_FUNCTION_ARGS
)
2330 RangeType
*r
= PG_GETARG_RANGE_P(0);
2331 MultirangeType
*mr
= PG_GETARG_MULTIRANGE_P(1);
2332 TypeCacheEntry
*typcache
;
2334 typcache
= multirange_get_typcache(fcinfo
, MultirangeTypeGetOid(mr
));
2336 PG_RETURN_BOOL(range_before_multirange_internal(typcache
->rngtype
, r
, mr
));
2340 multirange_before_range(PG_FUNCTION_ARGS
)
2342 MultirangeType
*mr
= PG_GETARG_MULTIRANGE_P(0);
2343 RangeType
*r
= PG_GETARG_RANGE_P(1);
2344 TypeCacheEntry
*typcache
;
2346 typcache
= multirange_get_typcache(fcinfo
, MultirangeTypeGetOid(mr
));
2348 PG_RETURN_BOOL(range_after_multirange_internal(typcache
->rngtype
, r
, mr
));
2352 multirange_before_multirange(PG_FUNCTION_ARGS
)
2354 MultirangeType
*mr1
= PG_GETARG_MULTIRANGE_P(0);
2355 MultirangeType
*mr2
= PG_GETARG_MULTIRANGE_P(1);
2356 TypeCacheEntry
*typcache
;
2358 typcache
= multirange_get_typcache(fcinfo
, MultirangeTypeGetOid(mr1
));
2360 PG_RETURN_BOOL(multirange_before_multirange_internal(typcache
->rngtype
, mr1
, mr2
));
2363 /* strictly right of? */
2365 range_after_multirange(PG_FUNCTION_ARGS
)
2367 RangeType
*r
= PG_GETARG_RANGE_P(0);
2368 MultirangeType
*mr
= PG_GETARG_MULTIRANGE_P(1);
2369 TypeCacheEntry
*typcache
;
2371 typcache
= multirange_get_typcache(fcinfo
, MultirangeTypeGetOid(mr
));
2373 PG_RETURN_BOOL(range_after_multirange_internal(typcache
->rngtype
, r
, mr
));
2377 multirange_after_range(PG_FUNCTION_ARGS
)
2379 MultirangeType
*mr
= PG_GETARG_MULTIRANGE_P(0);
2380 RangeType
*r
= PG_GETARG_RANGE_P(1);
2381 TypeCacheEntry
*typcache
;
2383 typcache
= multirange_get_typcache(fcinfo
, MultirangeTypeGetOid(mr
));
2385 PG_RETURN_BOOL(range_before_multirange_internal(typcache
->rngtype
, r
, mr
));
2389 multirange_after_multirange(PG_FUNCTION_ARGS
)
2391 MultirangeType
*mr1
= PG_GETARG_MULTIRANGE_P(0);
2392 MultirangeType
*mr2
= PG_GETARG_MULTIRANGE_P(1);
2393 TypeCacheEntry
*typcache
;
2395 typcache
= multirange_get_typcache(fcinfo
, MultirangeTypeGetOid(mr1
));
2397 PG_RETURN_BOOL(multirange_before_multirange_internal(typcache
->rngtype
, mr2
, mr1
));
2400 /* strictly left of? (internal version) */
2402 range_before_multirange_internal(TypeCacheEntry
*rangetyp
,
2404 const MultirangeType
*mr
)
2412 if (RangeIsEmpty(r
) || MultirangeIsEmpty(mr
))
2415 range_deserialize(rangetyp
, r
, &lower1
, &upper1
, &empty
);
2418 multirange_get_bounds(rangetyp
, mr
, 0, &lower2
, &upper2
);
2420 return (range_cmp_bounds(rangetyp
, &upper1
, &lower2
) < 0);
2424 multirange_before_multirange_internal(TypeCacheEntry
*rangetyp
,
2425 const MultirangeType
*mr1
,
2426 const MultirangeType
*mr2
)
2433 if (MultirangeIsEmpty(mr1
) || MultirangeIsEmpty(mr2
))
2436 multirange_get_bounds(rangetyp
, mr1
, mr1
->rangeCount
- 1,
2438 multirange_get_bounds(rangetyp
, mr2
, 0,
2441 return (range_cmp_bounds(rangetyp
, &upper1
, &lower2
) < 0);
2444 /* strictly right of? (internal version) */
2446 range_after_multirange_internal(TypeCacheEntry
*rangetyp
,
2448 const MultirangeType
*mr
)
2457 if (RangeIsEmpty(r
) || MultirangeIsEmpty(mr
))
2460 range_deserialize(rangetyp
, r
, &lower1
, &upper1
, &empty
);
2463 range_count
= mr
->rangeCount
;
2464 multirange_get_bounds(rangetyp
, mr
, range_count
- 1,
2467 return (range_cmp_bounds(rangetyp
, &lower1
, &upper2
) > 0);
2471 range_adjacent_multirange_internal(TypeCacheEntry
*rangetyp
,
2473 const MultirangeType
*mr
)
2482 if (RangeIsEmpty(r
) || MultirangeIsEmpty(mr
))
2485 range_deserialize(rangetyp
, r
, &lower1
, &upper1
, &empty
);
2488 range_count
= mr
->rangeCount
;
2489 multirange_get_bounds(rangetyp
, mr
, 0,
2492 if (bounds_adjacent(rangetyp
, upper1
, lower2
))
2495 if (range_count
> 1)
2496 multirange_get_bounds(rangetyp
, mr
, range_count
- 1,
2499 if (bounds_adjacent(rangetyp
, upper2
, lower1
))
2507 range_adjacent_multirange(PG_FUNCTION_ARGS
)
2509 RangeType
*r
= PG_GETARG_RANGE_P(0);
2510 MultirangeType
*mr
= PG_GETARG_MULTIRANGE_P(1);
2511 TypeCacheEntry
*typcache
;
2513 typcache
= multirange_get_typcache(fcinfo
, MultirangeTypeGetOid(mr
));
2515 PG_RETURN_BOOL(range_adjacent_multirange_internal(typcache
->rngtype
, r
, mr
));
2519 multirange_adjacent_range(PG_FUNCTION_ARGS
)
2521 MultirangeType
*mr
= PG_GETARG_MULTIRANGE_P(0);
2522 RangeType
*r
= PG_GETARG_RANGE_P(1);
2523 TypeCacheEntry
*typcache
;
2525 if (RangeIsEmpty(r
) || MultirangeIsEmpty(mr
))
2528 typcache
= multirange_get_typcache(fcinfo
, MultirangeTypeGetOid(mr
));
2530 PG_RETURN_BOOL(range_adjacent_multirange_internal(typcache
->rngtype
, r
, mr
));
2534 multirange_adjacent_multirange(PG_FUNCTION_ARGS
)
2536 MultirangeType
*mr1
= PG_GETARG_MULTIRANGE_P(0);
2537 MultirangeType
*mr2
= PG_GETARG_MULTIRANGE_P(1);
2538 TypeCacheEntry
*typcache
;
2546 if (MultirangeIsEmpty(mr1
) || MultirangeIsEmpty(mr2
))
2549 typcache
= multirange_get_typcache(fcinfo
, MultirangeTypeGetOid(mr1
));
2551 range_count1
= mr1
->rangeCount
;
2552 range_count2
= mr2
->rangeCount
;
2553 multirange_get_bounds(typcache
->rngtype
, mr1
, range_count1
- 1,
2555 multirange_get_bounds(typcache
->rngtype
, mr2
, 0,
2557 if (bounds_adjacent(typcache
->rngtype
, upper1
, lower2
))
2558 PG_RETURN_BOOL(true);
2560 if (range_count1
> 1)
2561 multirange_get_bounds(typcache
->rngtype
, mr1
, 0,
2563 if (range_count2
> 1)
2564 multirange_get_bounds(typcache
->rngtype
, mr2
, range_count2
- 1,
2566 if (bounds_adjacent(typcache
->rngtype
, upper2
, lower1
))
2567 PG_RETURN_BOOL(true);
2568 PG_RETURN_BOOL(false);
2573 /* btree comparator */
2575 multirange_cmp(PG_FUNCTION_ARGS
)
2577 MultirangeType
*mr1
= PG_GETARG_MULTIRANGE_P(0);
2578 MultirangeType
*mr2
= PG_GETARG_MULTIRANGE_P(1);
2579 int32 range_count_1
;
2580 int32 range_count_2
;
2581 int32 range_count_max
;
2583 TypeCacheEntry
*typcache
;
2584 int cmp
= 0; /* If both are empty we'll use this. */
2586 /* Different types should be prevented by ANYMULTIRANGE matching rules */
2587 if (MultirangeTypeGetOid(mr1
) != MultirangeTypeGetOid(mr2
))
2588 elog(ERROR
, "multirange types do not match");
2590 typcache
= multirange_get_typcache(fcinfo
, MultirangeTypeGetOid(mr1
));
2592 range_count_1
= mr1
->rangeCount
;
2593 range_count_2
= mr2
->rangeCount
;
2595 /* Loop over source data */
2596 range_count_max
= Max(range_count_1
, range_count_2
);
2597 for (i
= 0; i
< range_count_max
; i
++)
2605 * If one multirange is shorter, it's as if it had empty ranges at the
2606 * end to extend its length. An empty range compares earlier than any
2607 * other range, so the shorter multirange comes before the longer.
2608 * This is the same behavior as in other types, e.g. in strings 'aaa'
2611 if (i
>= range_count_1
)
2616 if (i
>= range_count_2
)
2622 multirange_get_bounds(typcache
->rngtype
, mr1
, i
, &lower1
, &upper1
);
2623 multirange_get_bounds(typcache
->rngtype
, mr2
, i
, &lower2
, &upper2
);
2625 cmp
= range_cmp_bounds(typcache
->rngtype
, &lower1
, &lower2
);
2627 cmp
= range_cmp_bounds(typcache
->rngtype
, &upper1
, &upper2
);
2632 PG_FREE_IF_COPY(mr1
, 0);
2633 PG_FREE_IF_COPY(mr2
, 1);
2635 PG_RETURN_INT32(cmp
);
2638 /* inequality operators using the multirange_cmp function */
2640 multirange_lt(PG_FUNCTION_ARGS
)
2642 int cmp
= multirange_cmp(fcinfo
);
2644 PG_RETURN_BOOL(cmp
< 0);
2648 multirange_le(PG_FUNCTION_ARGS
)
2650 int cmp
= multirange_cmp(fcinfo
);
2652 PG_RETURN_BOOL(cmp
<= 0);
2656 multirange_ge(PG_FUNCTION_ARGS
)
2658 int cmp
= multirange_cmp(fcinfo
);
2660 PG_RETURN_BOOL(cmp
>= 0);
2664 multirange_gt(PG_FUNCTION_ARGS
)
2666 int cmp
= multirange_cmp(fcinfo
);
2668 PG_RETURN_BOOL(cmp
> 0);
2671 /* multirange -> range functions */
2673 /* Find the smallest range that includes everything in the multirange */
2675 range_merge_from_multirange(PG_FUNCTION_ARGS
)
2677 MultirangeType
*mr
= PG_GETARG_MULTIRANGE_P(0);
2678 Oid mltrngtypoid
= MultirangeTypeGetOid(mr
);
2679 TypeCacheEntry
*typcache
;
2682 typcache
= multirange_get_typcache(fcinfo
, mltrngtypoid
);
2684 if (MultirangeIsEmpty(mr
))
2686 result
= make_empty_range(typcache
->rngtype
);
2688 else if (mr
->rangeCount
== 1)
2690 result
= multirange_get_range(typcache
->rngtype
, mr
, 0);
2694 RangeBound firstLower
,
2699 multirange_get_bounds(typcache
->rngtype
, mr
, 0,
2700 &firstLower
, &firstUpper
);
2701 multirange_get_bounds(typcache
->rngtype
, mr
, mr
->rangeCount
- 1,
2702 &lastLower
, &lastUpper
);
2704 result
= make_range(typcache
->rngtype
, &firstLower
, &lastUpper
,
2708 PG_RETURN_RANGE_P(result
);
2711 /* Turn multirange into a set of ranges */
2713 multirange_unnest(PG_FUNCTION_ARGS
)
2718 TypeCacheEntry
*typcache
;
2720 } multirange_unnest_fctx
;
2722 FuncCallContext
*funcctx
;
2723 multirange_unnest_fctx
*fctx
;
2724 MemoryContext oldcontext
;
2726 /* stuff done only on the first call of the function */
2727 if (SRF_IS_FIRSTCALL())
2731 /* create a function context for cross-call persistence */
2732 funcctx
= SRF_FIRSTCALL_INIT();
2735 * switch to memory context appropriate for multiple function calls
2737 oldcontext
= MemoryContextSwitchTo(funcctx
->multi_call_memory_ctx
);
2740 * Get the multirange value and detoast if needed. We can't do this
2741 * earlier because if we have to detoast, we want the detoasted copy
2742 * to be in multi_call_memory_ctx, so it will go away when we're done
2743 * and not before. (If no detoast happens, we assume the originally
2744 * passed multirange will stick around till then.)
2746 mr
= PG_GETARG_MULTIRANGE_P(0);
2748 /* allocate memory for user context */
2749 fctx
= (multirange_unnest_fctx
*) palloc(sizeof(multirange_unnest_fctx
));
2751 /* initialize state */
2754 fctx
->typcache
= lookup_type_cache(MultirangeTypeGetOid(mr
),
2755 TYPECACHE_MULTIRANGE_INFO
);
2757 funcctx
->user_fctx
= fctx
;
2758 MemoryContextSwitchTo(oldcontext
);
2761 /* stuff done on every call of the function */
2762 funcctx
= SRF_PERCALL_SETUP();
2763 fctx
= funcctx
->user_fctx
;
2765 if (fctx
->index
< fctx
->mr
->rangeCount
)
2769 range
= multirange_get_range(fctx
->typcache
->rngtype
,
2774 SRF_RETURN_NEXT(funcctx
, RangeTypePGetDatum(range
));
2778 /* do when there is no more left */
2779 SRF_RETURN_DONE(funcctx
);
2785 /* hash a multirange value */
2787 hash_multirange(PG_FUNCTION_ARGS
)
2789 MultirangeType
*mr
= PG_GETARG_MULTIRANGE_P(0);
2791 TypeCacheEntry
*typcache
,
2796 typcache
= multirange_get_typcache(fcinfo
, MultirangeTypeGetOid(mr
));
2797 scache
= typcache
->rngtype
->rngelemtype
;
2798 if (!OidIsValid(scache
->hash_proc_finfo
.fn_oid
))
2800 scache
= lookup_type_cache(scache
->type_id
,
2801 TYPECACHE_HASH_PROC_FINFO
);
2802 if (!OidIsValid(scache
->hash_proc_finfo
.fn_oid
))
2804 (errcode(ERRCODE_UNDEFINED_FUNCTION
),
2805 errmsg("could not identify a hash function for type %s",
2806 format_type_be(scache
->type_id
))));
2809 range_count
= mr
->rangeCount
;
2810 for (i
= 0; i
< range_count
; i
++)
2814 uint8 flags
= MultirangeGetFlagsPtr(mr
)[i
];
2819 multirange_get_bounds(typcache
->rngtype
, mr
, i
, &lower
, &upper
);
2821 if (RANGE_HAS_LBOUND(flags
))
2822 lower_hash
= DatumGetUInt32(FunctionCall1Coll(&scache
->hash_proc_finfo
,
2823 typcache
->rngtype
->rng_collation
,
2828 if (RANGE_HAS_UBOUND(flags
))
2829 upper_hash
= DatumGetUInt32(FunctionCall1Coll(&scache
->hash_proc_finfo
,
2830 typcache
->rngtype
->rng_collation
,
2835 /* Merge hashes of flags and bounds */
2836 range_hash
= hash_uint32((uint32
) flags
);
2837 range_hash
^= lower_hash
;
2838 range_hash
= pg_rotate_left32(range_hash
, 1);
2839 range_hash
^= upper_hash
;
2842 * Use the same approach as hash_array to combine the individual
2843 * elements' hash values:
2845 result
= (result
<< 5) - result
+ range_hash
;
2848 PG_FREE_IF_COPY(mr
, 0);
2850 PG_RETURN_UINT32(result
);
2854 * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
2855 * Otherwise, similar to hash_multirange.
2858 hash_multirange_extended(PG_FUNCTION_ARGS
)
2860 MultirangeType
*mr
= PG_GETARG_MULTIRANGE_P(0);
2861 Datum seed
= PG_GETARG_DATUM(1);
2863 TypeCacheEntry
*typcache
,
2868 typcache
= multirange_get_typcache(fcinfo
, MultirangeTypeGetOid(mr
));
2869 scache
= typcache
->rngtype
->rngelemtype
;
2870 if (!OidIsValid(scache
->hash_extended_proc_finfo
.fn_oid
))
2872 scache
= lookup_type_cache(scache
->type_id
,
2873 TYPECACHE_HASH_EXTENDED_PROC_FINFO
);
2874 if (!OidIsValid(scache
->hash_extended_proc_finfo
.fn_oid
))
2876 (errcode(ERRCODE_UNDEFINED_FUNCTION
),
2877 errmsg("could not identify a hash function for type %s",
2878 format_type_be(scache
->type_id
))));
2881 range_count
= mr
->rangeCount
;
2882 for (i
= 0; i
< range_count
; i
++)
2886 uint8 flags
= MultirangeGetFlagsPtr(mr
)[i
];
2891 multirange_get_bounds(typcache
->rngtype
, mr
, i
, &lower
, &upper
);
2893 if (RANGE_HAS_LBOUND(flags
))
2894 lower_hash
= DatumGetUInt64(FunctionCall2Coll(&scache
->hash_extended_proc_finfo
,
2895 typcache
->rngtype
->rng_collation
,
2901 if (RANGE_HAS_UBOUND(flags
))
2902 upper_hash
= DatumGetUInt64(FunctionCall2Coll(&scache
->hash_extended_proc_finfo
,
2903 typcache
->rngtype
->rng_collation
,
2909 /* Merge hashes of flags and bounds */
2910 range_hash
= DatumGetUInt64(hash_uint32_extended((uint32
) flags
,
2911 DatumGetInt64(seed
)));
2912 range_hash
^= lower_hash
;
2913 range_hash
= ROTATE_HIGH_AND_LOW_32BITS(range_hash
);
2914 range_hash
^= upper_hash
;
2917 * Use the same approach as hash_array to combine the individual
2918 * elements' hash values:
2920 result
= (result
<< 5) - result
+ range_hash
;
2923 PG_FREE_IF_COPY(mr
, 0);
2925 PG_RETURN_UINT64(result
);