1 /*-------------------------------------------------------------------------
2 *
3 * multirangetypes.c
4 * I/O functions, operators, and support functions for multirange types.
5 *
6 * The stored (serialized) format of a multirange value is:
7 *
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
12 * the second one.
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
15 * items.
16 *
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.
23 *
24 * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
25 * Portions Copyright (c) 1994, Regents of the University of California
26 *
27 *
28 * IDENTIFICATION
29 * src/backend/utils/adt/multirangetypes.c
30 *
31 *-------------------------------------------------------------------------
32 */
33 #include "postgres.h"
34
35 #include "access/tupmacs.h"
36 #include "common/hashfn.h"
37 #include "funcapi.h"
38 #include "lib/stringinfo.h"
39 #include "libpq/pqformat.h"
40 #include "miscadmin.h"
41 #include "utils/builtins.h"
42 #include "utils/lsyscache.h"
43 #include "utils/rangetypes.h"
44 #include "utils/multirangetypes.h"
45 #include "utils/array.h"
46 #include "utils/memutils.h"
47
48 /* fn_extra cache entry for one of the range I/O functions */
49 typedef struct MultirangeIOData
50 {
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 */
54 } MultirangeIOData;
55
56 typedef enum
57 {
58 MULTIRANGE_BEFORE_RANGE,
59 MULTIRANGE_IN_RANGE,
60 MULTIRANGE_IN_RANGE_ESCAPED,
61 MULTIRANGE_IN_RANGE_QUOTED,
62 MULTIRANGE_IN_RANGE_QUOTED_ESCAPED,
63 MULTIRANGE_AFTER_RANGE,
64 MULTIRANGE_FINISHED,
65 } MultirangeParseState;
66
67 /*
68 * Macros for accessing past MultirangeType parts of multirange: items, flags
69 * and boundaries.
70 */
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)))
79
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
84
85 typedef int (*multirange_bsearch_comparison) (TypeCacheEntry *typcache,
86 RangeBound *lower,
87 RangeBound *upper,
88 void *key,
89 bool *match);
90
91 static MultirangeIOData *get_multirange_io_data(FunctionCallInfo fcinfo,
92 Oid mltrngtypid,
93 IOFuncSelector func);
94 static int32 multirange_canonicalize(TypeCacheEntry *rangetyp,
95 int32 input_range_count,
96 RangeType **ranges);
97
98 /*
99 *----------------------------------------------------------
100 * I/O FUNCTIONS
101 *----------------------------------------------------------
102 */
103
104 /*
105 * Converts string to multirange.
106 *
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.
115 */
116 Datum
multirange_in(PG_FUNCTION_ARGS)117 multirange_in(PG_FUNCTION_ARGS)
118 {
119 char *input_str = PG_GETARG_CSTRING(0);
120 Oid mltrngtypoid = PG_GETARG_OID(1);
121 Oid typmod = PG_GETARG_INT32(2);
122 TypeCacheEntry *rangetyp;
123 int32 ranges_seen = 0;
124 int32 range_count = 0;
125 int32 range_capacity = 8;
126 RangeType *range;
127 RangeType **ranges = palloc(range_capacity * sizeof(RangeType *));
128 MultirangeIOData *cache;
129 MultirangeType *ret;
130 MultirangeParseState parse_state;
131 const char *ptr = input_str;
132 const char *range_str_begin = NULL;
133 int32 range_str_len;
134 char *range_str;
135
136 cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_input);
137 rangetyp = cache->typcache->rngtype;
138
139 /* consume whitespace */
140 while (*ptr != '\0' && isspace((unsigned char) *ptr))
141 ptr++;
142
143 if (*ptr == '{')
144 ptr++;
145 else
146 ereport(ERROR,
147 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
148 errmsg("malformed multirange literal: \"%s\"",
149 input_str),
150 errdetail("Missing left brace.")));
151
152 /* consume ranges */
153 parse_state = MULTIRANGE_BEFORE_RANGE;
154 for (; parse_state != MULTIRANGE_FINISHED; ptr++)
155 {
156 char ch = *ptr;
157
158 if (ch == '\0')
159 ereport(ERROR,
160 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
161 errmsg("malformed multirange literal: \"%s\"",
162 input_str),
163 errdetail("Unexpected end of input.")));
164
165 /* skip whitespace */
166 if (isspace((unsigned char) ch))
167 continue;
168
169 switch (parse_state)
170 {
171 case MULTIRANGE_BEFORE_RANGE:
172 if (ch == '[' || ch == '(')
173 {
174 range_str_begin = ptr;
175 parse_state = MULTIRANGE_IN_RANGE;
176 }
177 else if (ch == '}' && ranges_seen == 0)
178 parse_state = MULTIRANGE_FINISHED;
179 else if (pg_strncasecmp(ptr, RANGE_EMPTY_LITERAL,
180 strlen(RANGE_EMPTY_LITERAL)) == 0)
181 {
182 ranges_seen++;
183 /* nothing to do with an empty range */
184 ptr += strlen(RANGE_EMPTY_LITERAL) - 1;
185 parse_state = MULTIRANGE_AFTER_RANGE;
186 }
187 else
188 ereport(ERROR,
189 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
190 errmsg("malformed multirange literal: \"%s\"",
191 input_str),
192 errdetail("Expected range start.")));
193 break;
194 case MULTIRANGE_IN_RANGE:
195 if (ch == ']' || ch == ')')
196 {
197 range_str_len = ptr - range_str_begin + 1;
198 range_str = pnstrdup(range_str_begin, range_str_len);
199 if (range_capacity == range_count)
200 {
201 range_capacity *= 2;
202 ranges = (RangeType **)
203 repalloc(ranges, range_capacity * sizeof(RangeType *));
204 }
205 ranges_seen++;
206 range = DatumGetRangeTypeP(InputFunctionCall(&cache->typioproc,
207 range_str,
208 cache->typioparam,
209 typmod));
210 if (!RangeIsEmpty(range))
211 ranges[range_count++] = range;
212 parse_state = MULTIRANGE_AFTER_RANGE;
213 }
214 else
215 {
216 if (ch == '"')
217 parse_state = MULTIRANGE_IN_RANGE_QUOTED;
218 else if (ch == '\\')
219 parse_state = MULTIRANGE_IN_RANGE_ESCAPED;
220
221 /*
222 * We will include this character into range_str once we
223 * find the end of the range value.
224 */
225 }
226 break;
227 case MULTIRANGE_IN_RANGE_ESCAPED:
228
229 /*
230 * We will include this character into range_str once we find
231 * the end of the range value.
232 */
233 parse_state = MULTIRANGE_IN_RANGE;
234 break;
235 case MULTIRANGE_IN_RANGE_QUOTED:
236 if (ch == '"')
237 if (*(ptr + 1) == '"')
238 {
239 /* two quote marks means an escaped quote mark */
240 ptr++;
241 }
242 else
243 parse_state = MULTIRANGE_IN_RANGE;
244 else if (ch == '\\')
245 parse_state = MULTIRANGE_IN_RANGE_QUOTED_ESCAPED;
246
247 /*
248 * We will include this character into range_str once we find
249 * the end of the range value.
250 */
251 break;
252 case MULTIRANGE_AFTER_RANGE:
253 if (ch == ',')
254 parse_state = MULTIRANGE_BEFORE_RANGE;
255 else if (ch == '}')
256 parse_state = MULTIRANGE_FINISHED;
257 else
258 ereport(ERROR,
259 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
260 errmsg("malformed multirange literal: \"%s\"",
261 input_str),
262 errdetail("Expected comma or end of multirange.")));
263 break;
264 case MULTIRANGE_IN_RANGE_QUOTED_ESCAPED:
265
266 /*
267 * We will include this character into range_str once we find
268 * the end of the range value.
269 */
270 parse_state = MULTIRANGE_IN_RANGE_QUOTED;
271 break;
272 default:
273 elog(ERROR, "unknown parse state: %d", parse_state);
274 }
275 }
276
277 /* consume whitespace */
278 while (*ptr != '\0' && isspace((unsigned char) *ptr))
279 ptr++;
280
281 if (*ptr != '\0')
282 ereport(ERROR,
283 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
284 errmsg("malformed multirange literal: \"%s\"",
285 input_str),
286 errdetail("Junk after closing right brace.")));
287
288 ret = make_multirange(mltrngtypoid, rangetyp, range_count, ranges);
289 PG_RETURN_MULTIRANGE_P(ret);
290 }
291
292 Datum
multirange_out(PG_FUNCTION_ARGS)293 multirange_out(PG_FUNCTION_ARGS)
294 {
295 MultirangeType *multirange = PG_GETARG_MULTIRANGE_P(0);
296 Oid mltrngtypoid = MultirangeTypeGetOid(multirange);
297 MultirangeIOData *cache;
298 StringInfoData buf;
299 RangeType *range;
300 char *rangeStr;
301 int32 range_count;
302 int32 i;
303 RangeType **ranges;
304
305 cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_output);
306
307 initStringInfo(&buf);
308
309 appendStringInfoChar(&buf, '{');
310
311 multirange_deserialize(cache->typcache->rngtype, multirange, &range_count, &ranges);
312 for (i = 0; i < range_count; i++)
313 {
314 if (i > 0)
315 appendStringInfoChar(&buf, ',');
316 range = ranges[i];
317 rangeStr = OutputFunctionCall(&cache->typioproc, RangeTypePGetDatum(range));
318 appendStringInfoString(&buf, rangeStr);
319 }
320
321 appendStringInfoChar(&buf, '}');
322
323 PG_RETURN_CSTRING(buf.data);
324 }
325
326 /*
327 * Binary representation: First a int32-sized count of ranges, followed by
328 * ranges in their native binary representation.
329 */
330 Datum
multirange_recv(PG_FUNCTION_ARGS)331 multirange_recv(PG_FUNCTION_ARGS)
332 {
333 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
334 Oid mltrngtypoid = PG_GETARG_OID(1);
335 int32 typmod = PG_GETARG_INT32(2);
336 MultirangeIOData *cache;
337 uint32 range_count;
338 RangeType **ranges;
339 MultirangeType *ret;
340 StringInfoData tmpbuf;
341
342 cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_receive);
343
344 range_count = pq_getmsgint(buf, 4);
345 ranges = palloc(range_count * sizeof(RangeType *));
346
347 initStringInfo(&tmpbuf);
348 for (int i = 0; i < range_count; i++)
349 {
350 uint32 range_len = pq_getmsgint(buf, 4);
351 const char *range_data = pq_getmsgbytes(buf, range_len);
352
353 resetStringInfo(&tmpbuf);
354 appendBinaryStringInfo(&tmpbuf, range_data, range_len);
355
356 ranges[i] = DatumGetRangeTypeP(ReceiveFunctionCall(&cache->typioproc,
357 &tmpbuf,
358 cache->typioparam,
359 typmod));
360 }
361 pfree(tmpbuf.data);
362
363 pq_getmsgend(buf);
364
365 ret = make_multirange(mltrngtypoid, cache->typcache->rngtype,
366 range_count, ranges);
367 PG_RETURN_MULTIRANGE_P(ret);
368 }
369
370 Datum
multirange_send(PG_FUNCTION_ARGS)371 multirange_send(PG_FUNCTION_ARGS)
372 {
373 MultirangeType *multirange = PG_GETARG_MULTIRANGE_P(0);
374 Oid mltrngtypoid = MultirangeTypeGetOid(multirange);
375 StringInfo buf = makeStringInfo();
376 RangeType **ranges;
377 int32 range_count;
378 MultirangeIOData *cache;
379
380 cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_send);
381
382 /* construct output */
383 pq_begintypsend(buf);
384
385 pq_sendint32(buf, multirange->rangeCount);
386
387 multirange_deserialize(cache->typcache->rngtype, multirange, &range_count, &ranges);
388 for (int i = 0; i < range_count; i++)
389 {
390 Datum range;
391
392 range = RangeTypePGetDatum(ranges[i]);
393 range = PointerGetDatum(SendFunctionCall(&cache->typioproc, range));
394
395 pq_sendint32(buf, VARSIZE(range) - VARHDRSZ);
396 pq_sendbytes(buf, VARDATA(range), VARSIZE(range) - VARHDRSZ);
397 }
398
399 PG_RETURN_BYTEA_P(pq_endtypsend(buf));
400 }
401
402 /*
403 * get_multirange_io_data: get cached information needed for multirange type I/O
404 *
405 * The multirange I/O functions need a bit more cached info than other multirange
406 * functions, so they store a MultirangeIOData struct in fn_extra, not just a
407 * pointer to a type cache entry.
408 */
409 static MultirangeIOData *
get_multirange_io_data(FunctionCallInfo fcinfo,Oid mltrngtypid,IOFuncSelector func)410 get_multirange_io_data(FunctionCallInfo fcinfo, Oid mltrngtypid, IOFuncSelector func)
411 {
412 MultirangeIOData *cache = (MultirangeIOData *) fcinfo->flinfo->fn_extra;
413
414 if (cache == NULL || cache->typcache->type_id != mltrngtypid)
415 {
416 Oid typiofunc;
417 int16 typlen;
418 bool typbyval;
419 char typalign;
420 char typdelim;
421
422 cache = (MultirangeIOData *) MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
423 sizeof(MultirangeIOData));
424 cache->typcache = lookup_type_cache(mltrngtypid, TYPECACHE_MULTIRANGE_INFO);
425 if (cache->typcache->rngtype == NULL)
426 elog(ERROR, "type %u is not a multirange type", mltrngtypid);
427
428 /* get_type_io_data does more than we need, but is convenient */
429 get_type_io_data(cache->typcache->rngtype->type_id,
430 func,
431 &typlen,
432 &typbyval,
433 &typalign,
434 &typdelim,
435 &cache->typioparam,
436 &typiofunc);
437
438 if (!OidIsValid(typiofunc))
439 {
440 /* this could only happen for receive or send */
441 if (func == IOFunc_receive)
442 ereport(ERROR,
443 (errcode(ERRCODE_UNDEFINED_FUNCTION),
444 errmsg("no binary input function available for type %s",
445 format_type_be(cache->typcache->rngtype->type_id))));
446 else
447 ereport(ERROR,
448 (errcode(ERRCODE_UNDEFINED_FUNCTION),
449 errmsg("no binary output function available for type %s",
450 format_type_be(cache->typcache->rngtype->type_id))));
451 }
452 fmgr_info_cxt(typiofunc, &cache->typioproc,
453 fcinfo->flinfo->fn_mcxt);
454
455 fcinfo->flinfo->fn_extra = (void *) cache;
456 }
457
458 return cache;
459 }
460
461 /*
462 * Converts a list of arbitrary ranges into a list that is sorted and merged.
463 * Changes the contents of `ranges`.
464 *
465 * Returns the number of slots actually used, which may be less than
466 * input_range_count but never more.
467 *
468 * We assume that no input ranges are null, but empties are okay.
469 */
470 static int32
multirange_canonicalize(TypeCacheEntry * rangetyp,int32 input_range_count,RangeType ** ranges)471 multirange_canonicalize(TypeCacheEntry *rangetyp, int32 input_range_count,
472 RangeType **ranges)
473 {
474 RangeType *lastRange = NULL;
475 RangeType *currentRange;
476 int32 i;
477 int32 output_range_count = 0;
478
479 /* Sort the ranges so we can find the ones that overlap/meet. */
480 qsort_arg(ranges, input_range_count, sizeof(RangeType *), range_compare,
481 rangetyp);
482
483 /* Now merge where possible: */
484 for (i = 0; i < input_range_count; i++)
485 {
486 currentRange = ranges[i];
487 if (RangeIsEmpty(currentRange))
488 continue;
489
490 if (lastRange == NULL)
491 {
492 ranges[output_range_count++] = lastRange = currentRange;
493 continue;
494 }
495
496 /*
497 * range_adjacent_internal gives true if *either* A meets B or B meets
498 * A, which is not quite want we want, but we rely on the sorting
499 * above to rule out B meets A ever happening.
500 */
501 if (range_adjacent_internal(rangetyp, lastRange, currentRange))
502 {
503 /* The two ranges touch (without overlap), so merge them: */
504 ranges[output_range_count - 1] = lastRange =
505 range_union_internal(rangetyp, lastRange, currentRange, false);
506 }
507 else if (range_before_internal(rangetyp, lastRange, currentRange))
508 {
509 /* There's a gap, so make a new entry: */
510 lastRange = ranges[output_range_count] = currentRange;
511 output_range_count++;
512 }
513 else
514 {
515 /* They must overlap, so merge them: */
516 ranges[output_range_count - 1] = lastRange =
517 range_union_internal(rangetyp, lastRange, currentRange, true);
518 }
519 }
520
521 return output_range_count;
522 }
523
524 /*
525 *----------------------------------------------------------
526 * SUPPORT FUNCTIONS
527 *
528 * These functions aren't in pg_proc, but are useful for
529 * defining new generic multirange functions in C.
530 *----------------------------------------------------------
531 */
532
533 /*
534 * multirange_get_typcache: get cached information about a multirange type
535 *
536 * This is for use by multirange-related functions that follow the convention
537 * of using the fn_extra field as a pointer to the type cache entry for
538 * the multirange type. Functions that need to cache more information than
539 * that must fend for themselves.
540 */
541 TypeCacheEntry *
multirange_get_typcache(FunctionCallInfo fcinfo,Oid mltrngtypid)542 multirange_get_typcache(FunctionCallInfo fcinfo, Oid mltrngtypid)
543 {
544 TypeCacheEntry *typcache = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
545
546 if (typcache == NULL ||
547 typcache->type_id != mltrngtypid)
548 {
549 typcache = lookup_type_cache(mltrngtypid, TYPECACHE_MULTIRANGE_INFO);
550 if (typcache->rngtype == NULL)
551 elog(ERROR, "type %u is not a multirange type", mltrngtypid);
552 fcinfo->flinfo->fn_extra = (void *) typcache;
553 }
554
555 return typcache;
556 }
557
558
559 /*
560 * Estimate size occupied by serialized multirange.
561 */
562 static Size
multirange_size_estimate(TypeCacheEntry * rangetyp,int32 range_count,RangeType ** ranges)563 multirange_size_estimate(TypeCacheEntry *rangetyp, int32 range_count,
564 RangeType **ranges)
565 {
566 char elemalign = rangetyp->rngelemtype->typalign;
567 Size size;
568 int32 i;
569
570 /*
571 * Count space for MultirangeType struct, items and flags.
572 */
573 size = att_align_nominal(sizeof(MultirangeType) +
574 Max(range_count - 1, 0) * sizeof(uint32) +
575 range_count * sizeof(uint8), elemalign);
576
577 /* Count space for range bounds */
578 for (i = 0; i < range_count; i++)
579 size += att_align_nominal(VARSIZE(ranges[i]) -
580 sizeof(RangeType) -
581 sizeof(char), elemalign);
582
583 return size;
584 }
585
586 /*
587 * Write multirange data into pre-allocated space.
588 */
589 static void
write_multirange_data(MultirangeType * multirange,TypeCacheEntry * rangetyp,int32 range_count,RangeType ** ranges)590 write_multirange_data(MultirangeType *multirange, TypeCacheEntry *rangetyp,
591 int32 range_count, RangeType **ranges)
592 {
593 uint32 *items;
594 uint32 prev_offset = 0;
595 uint8 *flags;
596 int32 i;
597 Pointer begin,
598 ptr;
599 char elemalign = rangetyp->rngelemtype->typalign;
600
601 items = MultirangeGetItemsPtr(multirange);
602 flags = MultirangeGetFlagsPtr(multirange);
603 ptr = begin = MultirangeGetBoundariesPtr(multirange, elemalign);
604 for (i = 0; i < range_count; i++)
605 {
606 uint32 len;
607
608 if (i > 0)
609 {
610 /*
611 * Every range, except the first one, has an item. Every
612 * MULTIRANGE_ITEM_OFFSET_STRIDE item contains an offset, others
613 * contain lengths.
614 */
615 items[i - 1] = ptr - begin;
616 if ((i % MULTIRANGE_ITEM_OFFSET_STRIDE) != 0)
617 items[i - 1] -= prev_offset;
618 else
619 items[i - 1] |= MULTIRANGE_ITEM_OFF_BIT;
620 prev_offset = ptr - begin;
621 }
622 flags[i] = *((Pointer) ranges[i] + VARSIZE(ranges[i]) - sizeof(char));
623 len = VARSIZE(ranges[i]) - sizeof(RangeType) - sizeof(char);
624 memcpy(ptr, (Pointer) (ranges[i] + 1), len);
625 ptr += att_align_nominal(len, elemalign);
626 }
627 }
628
629
630 /*
631 * This serializes the multirange from a list of non-null ranges. It also
632 * sorts the ranges and merges any that touch. The ranges should already be
633 * detoasted, and there should be no NULLs. This should be used by most
634 * callers.
635 *
636 * Note that we may change the `ranges` parameter (the pointers, but not
637 * any already-existing RangeType contents).
638 */
639 MultirangeType *
make_multirange(Oid mltrngtypoid,TypeCacheEntry * rangetyp,int32 range_count,RangeType ** ranges)640 make_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp, int32 range_count,
641 RangeType **ranges)
642 {
643 MultirangeType *multirange;
644 Size size;
645
646 /* Sort and merge input ranges. */
647 range_count = multirange_canonicalize(rangetyp, range_count, ranges);
648
649 /* Note: zero-fill is required here, just as in heap tuples */
650 size = multirange_size_estimate(rangetyp, range_count, ranges);
651 multirange = palloc0(size);
652 SET_VARSIZE(multirange, size);
653
654 /* Now fill in the datum */
655 multirange->multirangetypid = mltrngtypoid;
656 multirange->rangeCount = range_count;
657
658 write_multirange_data(multirange, rangetyp, range_count, ranges);
659
660 return multirange;
661 }
662
663 /*
664 * Get offset of bounds values of the i'th range in the multirange.
665 */
666 static uint32
multirange_get_bounds_offset(const MultirangeType * multirange,int32 i)667 multirange_get_bounds_offset(const MultirangeType *multirange, int32 i)
668 {
669 uint32 *items = MultirangeGetItemsPtr(multirange);
670 uint32 offset = 0;
671
672 /*
673 * Summarize lengths till we meet an offset.
674 */
675 while (i > 0)
676 {
677 offset += MULTIRANGE_ITEM_GET_OFFLEN(items[i - 1]);
678 if (MULTIRANGE_ITEM_HAS_OFF(items[i - 1]))
679 break;
680 i--;
681 }
682 return offset;
683 }
684
685 /*
686 * Fetch the i'th range from the multirange.
687 */
688 RangeType *
multirange_get_range(TypeCacheEntry * rangetyp,const MultirangeType * multirange,int i)689 multirange_get_range(TypeCacheEntry *rangetyp,
690 const MultirangeType *multirange, int i)
691 {
692 uint32 offset;
693 uint8 flags;
694 Pointer begin,
695 ptr;
696 int16 typlen = rangetyp->rngelemtype->typlen;
697 char typalign = rangetyp->rngelemtype->typalign;
698 uint32 len;
699 RangeType *range;
700
701 Assert(i < multirange->rangeCount);
702
703 offset = multirange_get_bounds_offset(multirange, i);
704 flags = MultirangeGetFlagsPtr(multirange)[i];
705 ptr = begin = MultirangeGetBoundariesPtr(multirange, typalign) + offset;
706
707 /*
708 * Calculate the size of bound values. In principle, we could get offset
709 * of the next range bound values and calculate accordingly. But range
710 * bound values are aligned, so we have to walk the values to get the
711 * exact size.
712 */
713 if (RANGE_HAS_LBOUND(flags))
714 ptr = (Pointer) att_addlength_pointer(ptr, typlen, ptr);
715 if (RANGE_HAS_UBOUND(flags))
716 ptr = (Pointer) att_addlength_pointer(ptr, typlen, ptr);
717 len = (ptr - begin) + sizeof(RangeType) + sizeof(uint8);
718
719 range = palloc0(len);
720 SET_VARSIZE(range, len);
721 range->rangetypid = rangetyp->type_id;
722
723 memcpy(range + 1, begin, ptr - begin);
724 *((uint8 *) (range + 1) + (ptr - begin)) = flags;
725
726 return range;
727 }
728
729 /*
730 * Fetch bounds from the i'th range of the multirange. This is the shortcut for
731 * doing the same thing as multirange_get_range() + range_deserialize(), but
732 * performing fewer operations.
733 */
734 void
multirange_get_bounds(TypeCacheEntry * rangetyp,const MultirangeType * multirange,uint32 i,RangeBound * lower,RangeBound * upper)735 multirange_get_bounds(TypeCacheEntry *rangetyp,
736 const MultirangeType *multirange,
737 uint32 i, RangeBound *lower, RangeBound *upper)
738 {
739 uint32 offset;
740 uint8 flags;
741 Pointer ptr;
742 int16 typlen = rangetyp->rngelemtype->typlen;
743 char typalign = rangetyp->rngelemtype->typalign;
744 bool typbyval = rangetyp->rngelemtype->typbyval;
745 Datum lbound;
746 Datum ubound;
747
748 Assert(i < multirange->rangeCount);
749
750 offset = multirange_get_bounds_offset(multirange, i);
751 flags = MultirangeGetFlagsPtr(multirange)[i];
752 ptr = MultirangeGetBoundariesPtr(multirange, typalign) + offset;
753
754 /* multirange can't contain empty ranges */
755 Assert((flags & RANGE_EMPTY) == 0);
756
757 /* fetch lower bound, if any */
758 if (RANGE_HAS_LBOUND(flags))
759 {
760 /* att_align_pointer cannot be necessary here */
761 lbound = fetch_att(ptr, typbyval, typlen);
762 ptr = (Pointer) att_addlength_pointer(ptr, typlen, ptr);
763 }
764 else
765 lbound = (Datum) 0;
766
767 /* fetch upper bound, if any */
768 if (RANGE_HAS_UBOUND(flags))
769 {
770 ptr = (Pointer) att_align_pointer(ptr, typalign, typlen, ptr);
771 ubound = fetch_att(ptr, typbyval, typlen);
772 /* no need for att_addlength_pointer */
773 }
774 else
775 ubound = (Datum) 0;
776
777 /* emit results */
778 lower->val = lbound;
779 lower->infinite = (flags & RANGE_LB_INF) != 0;
780 lower->inclusive = (flags & RANGE_LB_INC) != 0;
781 lower->lower = true;
782
783 upper->val = ubound;
784 upper->infinite = (flags & RANGE_UB_INF) != 0;
785 upper->inclusive = (flags & RANGE_UB_INC) != 0;
786 upper->lower = false;
787 }
788
789 /*
790 * Construct union range from the multirange.
791 */
792 RangeType *
multirange_get_union_range(TypeCacheEntry * rangetyp,const MultirangeType * mr)793 multirange_get_union_range(TypeCacheEntry *rangetyp,
794 const MultirangeType *mr)
795 {
796 RangeBound lower,
797 upper,
798 tmp;
799
800 if (MultirangeIsEmpty(mr))
801 return make_empty_range(rangetyp);
802
803 multirange_get_bounds(rangetyp, mr, 0, &lower, &tmp);
804 multirange_get_bounds(rangetyp, mr, mr->rangeCount - 1, &tmp, &upper);
805
806 return make_range(rangetyp, &lower, &upper, false);
807 }
808
809
810 /*
811 * multirange_deserialize: deconstruct a multirange value
812 *
813 * NB: the given multirange object must be fully detoasted; it cannot have a
814 * short varlena header.
815 */
816 void
multirange_deserialize(TypeCacheEntry * rangetyp,const MultirangeType * multirange,int32 * range_count,RangeType *** ranges)817 multirange_deserialize(TypeCacheEntry *rangetyp,
818 const MultirangeType *multirange, int32 *range_count,
819 RangeType ***ranges)
820 {
821 *range_count = multirange->rangeCount;
822
823 /* Convert each ShortRangeType into a RangeType */
824 if (*range_count > 0)
825 {
826 int i;
827
828 *ranges = palloc(*range_count * sizeof(RangeType *));
829 for (i = 0; i < *range_count; i++)
830 (*ranges)[i] = multirange_get_range(rangetyp, multirange, i);
831 }
832 else
833 {
834 *ranges = NULL;
835 }
836 }
837
838 MultirangeType *
make_empty_multirange(Oid mltrngtypoid,TypeCacheEntry * rangetyp)839 make_empty_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp)
840 {
841 return make_multirange(mltrngtypoid, rangetyp, 0, NULL);
842 }
843
844 /*
845 * Similar to range_overlaps_internal(), but takes range bounds instead of
846 * ranges as arguments.
847 */
848 static bool
range_bounds_overlaps(TypeCacheEntry * typcache,RangeBound * lower1,RangeBound * upper1,RangeBound * lower2,RangeBound * upper2)849 range_bounds_overlaps(TypeCacheEntry *typcache,
850 RangeBound *lower1, RangeBound *upper1,
851 RangeBound *lower2, RangeBound *upper2)
852 {
853 if (range_cmp_bounds(typcache, lower1, lower2) >= 0 &&
854 range_cmp_bounds(typcache, lower1, upper2) <= 0)
855 return true;
856
857 if (range_cmp_bounds(typcache, lower2, lower1) >= 0 &&
858 range_cmp_bounds(typcache, lower2, upper1) <= 0)
859 return true;
860
861 return false;
862 }
863
864 /*
865 * Similar to range_contains_internal(), but takes range bounds instead of
866 * ranges as arguments.
867 */
868 static bool
range_bounds_contains(TypeCacheEntry * typcache,RangeBound * lower1,RangeBound * upper1,RangeBound * lower2,RangeBound * upper2)869 range_bounds_contains(TypeCacheEntry *typcache,
870 RangeBound *lower1, RangeBound *upper1,
871 RangeBound *lower2, RangeBound *upper2)
872 {
873 if (range_cmp_bounds(typcache, lower1, lower2) <= 0 &&
874 range_cmp_bounds(typcache, upper1, upper2) >= 0)
875 return true;
876
877 return false;
878 }
879
880 /*
881 * Check if the given key matches any range in multirange using binary search.
882 * If the required range isn't found, that counts as a mismatch. When the
883 * required range is found, the comparison function can still report this as
884 * either match or mismatch. For instance, if we search for containment, we can
885 * found a range, which is overlapping but not containing the key range, and
886 * that would count as a mismatch.
887 */
888 static bool
multirange_bsearch_match(TypeCacheEntry * typcache,const MultirangeType * mr,void * key,multirange_bsearch_comparison cmp_func)889 multirange_bsearch_match(TypeCacheEntry *typcache, const MultirangeType *mr,
890 void *key, multirange_bsearch_comparison cmp_func)
891 {
892 uint32 l,
893 u,
894 idx;
895 int comparison;
896 bool match = false;
897
898 l = 0;
899 u = mr->rangeCount;
900 while (l < u)
901 {
902 RangeBound lower,
903 upper;
904
905 idx = (l + u) / 2;
906 multirange_get_bounds(typcache, mr, idx, &lower, &upper);
907 comparison = (*cmp_func) (typcache, &lower, &upper, key, &match);
908
909 if (comparison < 0)
910 u = idx;
911 else if (comparison > 0)
912 l = idx + 1;
913 else
914 return match;
915 }
916
917 return false;
918 }
919
920 /*
921 *----------------------------------------------------------
922 * GENERIC FUNCTIONS
923 *----------------------------------------------------------
924 */
925
926 /*
927 * Construct multirange value from zero or more ranges. Since this is a
928 * variadic function we get passed an array. The array must contain ranges
929 * that match our return value, and there must be no NULLs.
930 */
931 Datum
multirange_constructor2(PG_FUNCTION_ARGS)932 multirange_constructor2(PG_FUNCTION_ARGS)
933 {
934 Oid mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
935 Oid rngtypid;
936 TypeCacheEntry *typcache;
937 TypeCacheEntry *rangetyp;
938 ArrayType *rangeArray;
939 int range_count;
940 Datum *elements;
941 bool *nulls;
942 RangeType **ranges;
943 int dims;
944 int i;
945
946 typcache = multirange_get_typcache(fcinfo, mltrngtypid);
947 rangetyp = typcache->rngtype;
948
949 /*
950 * A no-arg invocation should call multirange_constructor0 instead, but
951 * returning an empty range is what that does.
952 */
953
954 if (PG_NARGS() == 0)
955 PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 0, NULL));
956
957 /*
958 * This check should be guaranteed by our signature, but let's do it just
959 * in case.
960 */
961
962 if (PG_ARGISNULL(0))
963 elog(ERROR,
964 "multirange values cannot contain null members");
965
966 rangeArray = PG_GETARG_ARRAYTYPE_P(0);
967
968 dims = ARR_NDIM(rangeArray);
969 if (dims > 1)
970 ereport(ERROR,
971 (errcode(ERRCODE_CARDINALITY_VIOLATION),
972 errmsg("multiranges cannot be constructed from multidimensional arrays")));
973
974 rngtypid = ARR_ELEMTYPE(rangeArray);
975 if (rngtypid != rangetyp->type_id)
976 elog(ERROR, "type %u does not match constructor type", rngtypid);
977
978 /*
979 * Be careful: we can still be called with zero ranges, like this:
980 * `int4multirange(variadic '{}'::int4range[])
981 */
982 if (dims == 0)
983 {
984 range_count = 0;
985 ranges = NULL;
986 }
987 else
988 {
989 deconstruct_array(rangeArray, rngtypid, rangetyp->typlen, rangetyp->typbyval,
990 rangetyp->typalign, &elements, &nulls, &range_count);
991
992 ranges = palloc0(range_count * sizeof(RangeType *));
993 for (i = 0; i < range_count; i++)
994 {
995 if (nulls[i])
996 ereport(ERROR,
997 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
998 errmsg("multirange values cannot contain null members")));
999
1000 /* make_multirange will do its own copy */
1001 ranges[i] = DatumGetRangeTypeP(elements[i]);
1002 }
1003 }
1004
1005 PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, range_count, ranges));
1006 }
1007
1008 /*
1009 * Construct multirange value from a single range. It'd be nice if we could
1010 * just use multirange_constructor2 for this case, but we need a non-variadic
1011 * single-arg function to let us define a CAST from a range to its multirange.
1012 */
1013 Datum
multirange_constructor1(PG_FUNCTION_ARGS)1014 multirange_constructor1(PG_FUNCTION_ARGS)
1015 {
1016 Oid mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
1017 Oid rngtypid;
1018 TypeCacheEntry *typcache;
1019 TypeCacheEntry *rangetyp;
1020 RangeType *range;
1021
1022 typcache = multirange_get_typcache(fcinfo, mltrngtypid);
1023 rangetyp = typcache->rngtype;
1024
1025 /*
1026 * This check should be guaranteed by our signature, but let's do it just
1027 * in case.
1028 */
1029
1030 if (PG_ARGISNULL(0))
1031 elog(ERROR,
1032 "multirange values cannot contain null members");
1033
1034 range = PG_GETARG_RANGE_P(0);
1035
1036 /* Make sure the range type matches. */
1037 rngtypid = RangeTypeGetOid(range);
1038 if (rngtypid != rangetyp->type_id)
1039 elog(ERROR, "type %u does not match constructor type", rngtypid);
1040
1041 PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 1, &range));
1042 }
1043
1044 /*
1045 * Constructor just like multirange_constructor1, but opr_sanity gets angry
1046 * if the same internal function handles multiple functions with different arg
1047 * counts.
1048 */
1049 Datum
multirange_constructor0(PG_FUNCTION_ARGS)1050 multirange_constructor0(PG_FUNCTION_ARGS)
1051 {
1052 Oid mltrngtypid;
1053 TypeCacheEntry *typcache;
1054 TypeCacheEntry *rangetyp;
1055
1056 /* This should always be called without arguments */
1057 if (PG_NARGS() != 0)
1058 elog(ERROR,
1059 "niladic multirange constructor must not receive arguments");
1060
1061 mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
1062 typcache = multirange_get_typcache(fcinfo, mltrngtypid);
1063 rangetyp = typcache->rngtype;
1064
1065 PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 0, NULL));
1066 }
1067
1068
1069 /* multirange, multirange -> multirange type functions */
1070
1071 /* multirange union */
1072 Datum
multirange_union(PG_FUNCTION_ARGS)1073 multirange_union(PG_FUNCTION_ARGS)
1074 {
1075 MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
1076 MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
1077 TypeCacheEntry *typcache;
1078 int32 range_count1;
1079 int32 range_count2;
1080 int32 range_count3;
1081 RangeType **ranges1;
1082 RangeType **ranges2;
1083 RangeType **ranges3;
1084
1085 if (MultirangeIsEmpty(mr1))
1086 PG_RETURN_MULTIRANGE_P(mr2);
1087 if (MultirangeIsEmpty(mr2))
1088 PG_RETURN_MULTIRANGE_P(mr1);
1089
1090 typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
1091
1092 multirange_deserialize(typcache->rngtype, mr1, &range_count1, &ranges1);
1093 multirange_deserialize(typcache->rngtype, mr2, &range_count2, &ranges2);
1094
1095 range_count3 = range_count1 + range_count2;
1096 ranges3 = palloc0(range_count3 * sizeof(RangeType *));
1097 memcpy(ranges3, ranges1, range_count1 * sizeof(RangeType *));
1098 memcpy(ranges3 + range_count1, ranges2, range_count2 * sizeof(RangeType *));
1099 PG_RETURN_MULTIRANGE_P(make_multirange(typcache->type_id, typcache->rngtype,
1100 range_count3, ranges3));
1101 }
1102
1103 /* multirange minus */
1104 Datum
multirange_minus(PG_FUNCTION_ARGS)1105 multirange_minus(PG_FUNCTION_ARGS)
1106 {
1107 MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
1108 MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
1109 Oid mltrngtypoid = MultirangeTypeGetOid(mr1);
1110 TypeCacheEntry *typcache;
1111 TypeCacheEntry *rangetyp;
1112 int32 range_count1;
1113 int32 range_count2;
1114 RangeType **ranges1;
1115 RangeType **ranges2;
1116
1117 typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
1118 rangetyp = typcache->rngtype;
1119
1120 if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
1121 PG_RETURN_MULTIRANGE_P(mr1);
1122
1123 multirange_deserialize(typcache->rngtype, mr1, &range_count1, &ranges1);
1124 multirange_deserialize(typcache->rngtype, mr2, &range_count2, &ranges2);
1125
1126 PG_RETURN_MULTIRANGE_P(multirange_minus_internal(mltrngtypoid,
1127 rangetyp,
1128 range_count1,
1129 ranges1,
1130 range_count2,
1131 ranges2));
1132 }
1133
1134 MultirangeType *
multirange_minus_internal(Oid mltrngtypoid,TypeCacheEntry * rangetyp,int32 range_count1,RangeType ** ranges1,int32 range_count2,RangeType ** ranges2)1135 multirange_minus_internal(Oid mltrngtypoid, TypeCacheEntry *rangetyp,
1136 int32 range_count1, RangeType **ranges1,
1137 int32 range_count2, RangeType **ranges2)
1138 {
1139 RangeType *r1;
1140 RangeType *r2;
1141 RangeType **ranges3;
1142 int32 range_count3;
1143 int32 i1;
1144 int32 i2;
1145
1146 /*
1147 * Worst case: every range in ranges1 makes a different cut to some range
1148 * in ranges2.
1149 */
1150 ranges3 = palloc0((range_count1 + range_count2) * sizeof(RangeType *));
1151 range_count3 = 0;
1152
1153 /*
1154 * For each range in mr1, keep subtracting until it's gone or the ranges
1155 * in mr2 have passed it. After a subtraction we assign what's left back
1156 * to r1. The parallel progress through mr1 and mr2 is similar to
1157 * multirange_overlaps_multirange_internal.
1158 */
1159 r2 = ranges2[0];
1160 for (i1 = 0, i2 = 0; i1 < range_count1; i1++)
1161 {
1162 r1 = ranges1[i1];
1163
1164 /* Discard r2s while r2 << r1 */
1165 while (r2 != NULL && range_before_internal(rangetyp, r2, r1))
1166 {
1167 r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
1168 }
1169
1170 while (r2 != NULL)
1171 {
1172 if (range_split_internal(rangetyp, r1, r2, &ranges3[range_count3], &r1))
1173 {
1174 /*
1175 * If r2 takes a bite out of the middle of r1, we need two
1176 * outputs
1177 */
1178 range_count3++;
1179 r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
1180
1181 }
1182 else if (range_overlaps_internal(rangetyp, r1, r2))
1183 {
1184 /*
1185 * If r2 overlaps r1, replace r1 with r1 - r2.
1186 */
1187 r1 = range_minus_internal(rangetyp, r1, r2);
1188
1189 /*
1190 * If r2 goes past r1, then we need to stay with it, in case
1191 * it hits future r1s. Otherwise we need to keep r1, in case
1192 * future r2s hit it. Since we already subtracted, there's no
1193 * point in using the overright/overleft calls.
1194 */
1195 if (RangeIsEmpty(r1) || range_before_internal(rangetyp, r1, r2))
1196 break;
1197 else
1198 r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
1199
1200 }
1201 else
1202 {
1203 /*
1204 * This and all future r2s are past r1, so keep them. Also
1205 * assign whatever is left of r1 to the result.
1206 */
1207 break;
1208 }
1209 }
1210
1211 /*
1212 * Nothing else can remove anything from r1, so keep it. Even if r1 is
1213 * empty here, make_multirange will remove it.
1214 */
1215 ranges3[range_count3++] = r1;
1216 }
1217
1218 return make_multirange(mltrngtypoid, rangetyp, range_count3, ranges3);
1219 }
1220
1221 /* multirange intersection */
1222 Datum
multirange_intersect(PG_FUNCTION_ARGS)1223 multirange_intersect(PG_FUNCTION_ARGS)
1224 {
1225 MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
1226 MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
1227 Oid mltrngtypoid = MultirangeTypeGetOid(mr1);
1228 TypeCacheEntry *typcache;
1229 TypeCacheEntry *rangetyp;
1230 int32 range_count1;
1231 int32 range_count2;
1232 RangeType **ranges1;
1233 RangeType **ranges2;
1234
1235 typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
1236 rangetyp = typcache->rngtype;
1237
1238 if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
1239 PG_RETURN_MULTIRANGE_P(make_empty_multirange(mltrngtypoid, rangetyp));
1240
1241 multirange_deserialize(rangetyp, mr1, &range_count1, &ranges1);
1242 multirange_deserialize(rangetyp, mr2, &range_count2, &ranges2);
1243
1244 PG_RETURN_MULTIRANGE_P(multirange_intersect_internal(mltrngtypoid,
1245 rangetyp,
1246 range_count1,
1247 ranges1,
1248 range_count2,
1249 ranges2));
1250 }
1251
1252 MultirangeType *
multirange_intersect_internal(Oid mltrngtypoid,TypeCacheEntry * rangetyp,int32 range_count1,RangeType ** ranges1,int32 range_count2,RangeType ** ranges2)1253 multirange_intersect_internal(Oid mltrngtypoid, TypeCacheEntry *rangetyp,
1254 int32 range_count1, RangeType **ranges1,
1255 int32 range_count2, RangeType **ranges2)
1256 {
1257 RangeType *r1;
1258 RangeType *r2;
1259 RangeType **ranges3;
1260 int32 range_count3;
1261 int32 i1;
1262 int32 i2;
1263
1264 if (range_count1 == 0 || range_count2 == 0)
1265 return make_multirange(mltrngtypoid, rangetyp, 0, NULL);
1266
1267 /*-----------------------------------------------
1268 * Worst case is a stitching pattern like this:
1269 *
1270 * mr1: --- --- --- ---
1271 * mr2: --- --- ---
1272 * mr3: - - - - - -
1273 *
1274 * That seems to be range_count1 + range_count2 - 1,
1275 * but one extra won't hurt.
1276 *-----------------------------------------------
1277 */
1278 ranges3 = palloc0((range_count1 + range_count2) * sizeof(RangeType *));
1279 range_count3 = 0;
1280
1281 /*
1282 * For each range in mr1, keep intersecting until the ranges in mr2 have
1283 * passed it. The parallel progress through mr1 and mr2 is similar to
1284 * multirange_minus_multirange_internal, but we don't have to assign back
1285 * to r1.
1286 */
1287 r2 = ranges2[0];
1288 for (i1 = 0, i2 = 0; i1 < range_count1; i1++)
1289 {
1290 r1 = ranges1[i1];
1291
1292 /* Discard r2s while r2 << r1 */
1293 while (r2 != NULL && range_before_internal(rangetyp, r2, r1))
1294 {
1295 r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
1296 }
1297
1298 while (r2 != NULL)
1299 {
1300 if (range_overlaps_internal(rangetyp, r1, r2))
1301 {
1302 /* Keep the overlapping part */
1303 ranges3[range_count3++] = range_intersect_internal(rangetyp, r1, r2);
1304
1305 /* If we "used up" all of r2, go to the next one... */
1306 if (range_overleft_internal(rangetyp, r2, r1))
1307 r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
1308
1309 /* ...otherwise go to the next r1 */
1310 else
1311 break;
1312 }
1313 else
1314 /* We're past r1, so move to the next one */
1315 break;
1316 }
1317
1318 /* If we're out of r2s, there can be no more intersections */
1319 if (r2 == NULL)
1320 break;
1321 }
1322
1323 return make_multirange(mltrngtypoid, rangetyp, range_count3, ranges3);
1324 }
1325
1326 /*
1327 * range_agg_transfn: combine adjacent/overlapping ranges.
1328 *
1329 * All we do here is gather the input ranges into an array
1330 * so that the finalfn can sort and combine them.
1331 */
1332 Datum
range_agg_transfn(PG_FUNCTION_ARGS)1333 range_agg_transfn(PG_FUNCTION_ARGS)
1334 {
1335 MemoryContext aggContext;
1336 Oid rngtypoid;
1337 ArrayBuildState *state;
1338
1339 if (!AggCheckCallContext(fcinfo, &aggContext))
1340 elog(ERROR, "range_agg_transfn called in non-aggregate context");
1341
1342 rngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
1343 if (!type_is_range(rngtypoid))
1344 ereport(ERROR,
1345 (errcode(ERRCODE_DATATYPE_MISMATCH),
1346 errmsg("range_agg must be called with a range")));
1347
1348 if (PG_ARGISNULL(0))
1349 state = initArrayResult(rngtypoid, aggContext, false);
1350 else
1351 state = (ArrayBuildState *) PG_GETARG_POINTER(0);
1352
1353 /* skip NULLs */
1354 if (!PG_ARGISNULL(1))
1355 accumArrayResult(state, PG_GETARG_DATUM(1), false, rngtypoid, aggContext);
1356
1357 PG_RETURN_POINTER(state);
1358 }
1359
1360 /*
1361 * range_agg_finalfn: use our internal array to merge touching ranges.
1362 */
1363 Datum
range_agg_finalfn(PG_FUNCTION_ARGS)1364 range_agg_finalfn(PG_FUNCTION_ARGS)
1365 {
1366 MemoryContext aggContext;
1367 Oid mltrngtypoid;
1368 TypeCacheEntry *typcache;
1369 ArrayBuildState *state;
1370 int32 range_count;
1371 RangeType **ranges;
1372 int i;
1373
1374 if (!AggCheckCallContext(fcinfo, &aggContext))
1375 elog(ERROR, "range_agg_finalfn called in non-aggregate context");
1376
1377 state = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
1378 if (state == NULL)
1379 /* This shouldn't be possible, but just in case.... */
1380 PG_RETURN_NULL();
1381
1382 /* Also return NULL if we had zero inputs, like other aggregates */
1383 range_count = state->nelems;
1384 if (range_count == 0)
1385 PG_RETURN_NULL();
1386
1387 mltrngtypoid = get_fn_expr_rettype(fcinfo->flinfo);
1388 typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
1389
1390 ranges = palloc0(range_count * sizeof(RangeType *));
1391 for (i = 0; i < range_count; i++)
1392 ranges[i] = DatumGetRangeTypeP(state->dvalues[i]);
1393
1394 PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypoid, typcache->rngtype, range_count, ranges));
1395 }
1396
1397 Datum
multirange_intersect_agg_transfn(PG_FUNCTION_ARGS)1398 multirange_intersect_agg_transfn(PG_FUNCTION_ARGS)
1399 {
1400 MemoryContext aggContext;
1401 Oid mltrngtypoid;
1402 TypeCacheEntry *typcache;
1403 MultirangeType *result;
1404 MultirangeType *current;
1405 int32 range_count1;
1406 int32 range_count2;
1407 RangeType **ranges1;
1408 RangeType **ranges2;
1409
1410 if (!AggCheckCallContext(fcinfo, &aggContext))
1411 elog(ERROR, "multirange_intersect_agg_transfn called in non-aggregate context");
1412
1413 mltrngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
1414 if (!type_is_multirange(mltrngtypoid))
1415 ereport(ERROR,
1416 (errcode(ERRCODE_DATATYPE_MISMATCH),
1417 errmsg("range_intersect_agg must be called with a multirange")));
1418
1419 typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
1420
1421 /* strictness ensures these are non-null */
1422 result = PG_GETARG_MULTIRANGE_P(0);
1423 current = PG_GETARG_MULTIRANGE_P(1);
1424
1425 multirange_deserialize(typcache->rngtype, result, &range_count1, &ranges1);
1426 multirange_deserialize(typcache->rngtype, current, &range_count2, &ranges2);
1427
1428 result = multirange_intersect_internal(mltrngtypoid,
1429 typcache->rngtype,
1430 range_count1,
1431 ranges1,
1432 range_count2,
1433 ranges2);
1434 PG_RETURN_RANGE_P(result);
1435 }
1436
1437
1438 /* multirange -> element type functions */
1439
1440 /* extract lower bound value */
1441 Datum
multirange_lower(PG_FUNCTION_ARGS)1442 multirange_lower(PG_FUNCTION_ARGS)
1443 {
1444 MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
1445 TypeCacheEntry *typcache;
1446 RangeBound lower;
1447 RangeBound upper;
1448
1449 if (MultirangeIsEmpty(mr))
1450 PG_RETURN_NULL();
1451
1452 typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
1453
1454 multirange_get_bounds(typcache->rngtype, mr, 0,
1455 &lower, &upper);
1456
1457 if (!lower.infinite)
1458 PG_RETURN_DATUM(lower.val);
1459 else
1460 PG_RETURN_NULL();
1461 }
1462
1463 /* extract upper bound value */
1464 Datum
multirange_upper(PG_FUNCTION_ARGS)1465 multirange_upper(PG_FUNCTION_ARGS)
1466 {
1467 MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
1468 TypeCacheEntry *typcache;
1469 RangeBound lower;
1470 RangeBound upper;
1471
1472 if (MultirangeIsEmpty(mr))
1473 PG_RETURN_NULL();
1474
1475 typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
1476
1477 multirange_get_bounds(typcache->rngtype, mr, mr->rangeCount - 1,
1478 &lower, &upper);
1479
1480 if (!upper.infinite)
1481 PG_RETURN_DATUM(upper.val);
1482 else
1483 PG_RETURN_NULL();
1484 }
1485
1486
1487 /* multirange -> bool functions */
1488
1489 /* is multirange empty? */
1490 Datum
multirange_empty(PG_FUNCTION_ARGS)1491 multirange_empty(PG_FUNCTION_ARGS)
1492 {
1493 MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
1494
1495 PG_RETURN_BOOL(MultirangeIsEmpty(mr));
1496 }
1497
1498 /* is lower bound inclusive? */
1499 Datum
multirange_lower_inc(PG_FUNCTION_ARGS)1500 multirange_lower_inc(PG_FUNCTION_ARGS)
1501 {
1502 MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
1503 TypeCacheEntry *typcache;
1504 RangeBound lower;
1505 RangeBound upper;
1506
1507 if (MultirangeIsEmpty(mr))
1508 PG_RETURN_BOOL(false);
1509
1510 typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
1511 multirange_get_bounds(typcache->rngtype, mr, 0,
1512 &lower, &upper);
1513
1514 PG_RETURN_BOOL(lower.inclusive);
1515 }
1516
1517 /* is upper bound inclusive? */
1518 Datum
multirange_upper_inc(PG_FUNCTION_ARGS)1519 multirange_upper_inc(PG_FUNCTION_ARGS)
1520 {
1521 MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
1522 TypeCacheEntry *typcache;
1523 RangeBound lower;
1524 RangeBound upper;
1525
1526 if (MultirangeIsEmpty(mr))
1527 PG_RETURN_BOOL(false);
1528
1529 typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
1530 multirange_get_bounds(typcache->rngtype, mr, mr->rangeCount - 1,
1531 &lower, &upper);
1532
1533 PG_RETURN_BOOL(upper.inclusive);
1534 }
1535
1536 /* is lower bound infinite? */
1537 Datum
multirange_lower_inf(PG_FUNCTION_ARGS)1538 multirange_lower_inf(PG_FUNCTION_ARGS)
1539 {
1540 MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
1541 TypeCacheEntry *typcache;
1542 RangeBound lower;
1543 RangeBound upper;
1544
1545 if (MultirangeIsEmpty(mr))
1546 PG_RETURN_BOOL(false);
1547
1548 typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
1549 multirange_get_bounds(typcache->rngtype, mr, 0,
1550 &lower, &upper);
1551
1552 PG_RETURN_BOOL(lower.infinite);
1553 }
1554
1555 /* is upper bound infinite? */
1556 Datum
multirange_upper_inf(PG_FUNCTION_ARGS)1557 multirange_upper_inf(PG_FUNCTION_ARGS)
1558 {
1559 MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
1560 TypeCacheEntry *typcache;
1561 RangeBound lower;
1562 RangeBound upper;
1563
1564 if (MultirangeIsEmpty(mr))
1565 PG_RETURN_BOOL(false);
1566
1567 typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
1568 multirange_get_bounds(typcache->rngtype, mr, mr->rangeCount - 1,
1569 &lower, &upper);
1570
1571 PG_RETURN_BOOL(upper.infinite);
1572 }
1573
1574
1575
1576 /* multirange, element -> bool functions */
1577
1578 /* contains? */
1579 Datum
multirange_contains_elem(PG_FUNCTION_ARGS)1580 multirange_contains_elem(PG_FUNCTION_ARGS)
1581 {
1582 MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
1583 Datum val = PG_GETARG_DATUM(1);
1584 TypeCacheEntry *typcache;
1585
1586 typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
1587
1588 PG_RETURN_BOOL(multirange_contains_elem_internal(typcache->rngtype, mr, val));
1589 }
1590
1591 /* contained by? */
1592 Datum
elem_contained_by_multirange(PG_FUNCTION_ARGS)1593 elem_contained_by_multirange(PG_FUNCTION_ARGS)
1594 {
1595 Datum val = PG_GETARG_DATUM(0);
1596 MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
1597 TypeCacheEntry *typcache;
1598
1599 typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
1600
1601 PG_RETURN_BOOL(multirange_contains_elem_internal(typcache->rngtype, mr, val));
1602 }
1603
1604 /*
1605 * Comparison function for checking if any range of multirange contains given
1606 * key element using binary search.
1607 */
1608 static int
multirange_elem_bsearch_comparison(TypeCacheEntry * typcache,RangeBound * lower,RangeBound * upper,void * key,bool * match)1609 multirange_elem_bsearch_comparison(TypeCacheEntry *typcache,
1610 RangeBound *lower, RangeBound *upper,
1611 void *key, bool *match)
1612 {
1613 Datum val = *((Datum *) key);
1614 int cmp;
1615
1616 if (!lower->infinite)
1617 {
1618 cmp = DatumGetInt32(FunctionCall2Coll(&typcache->rng_cmp_proc_finfo,
1619 typcache->rng_collation,
1620 lower->val, val));
1621 if (cmp > 0 || (cmp == 0 && !lower->inclusive))
1622 return -1;
1623 }
1624
1625 if (!upper->infinite)
1626 {
1627 cmp = DatumGetInt32(FunctionCall2Coll(&typcache->rng_cmp_proc_finfo,
1628 typcache->rng_collation,
1629 upper->val, val));
1630 if (cmp < 0 || (cmp == 0 && !upper->inclusive))
1631 return 1;
1632 }
1633
1634 *match = true;
1635 return 0;
1636 }
1637
1638 /*
1639 * Test whether multirange mr contains a specific element value.
1640 */
1641 bool
multirange_contains_elem_internal(TypeCacheEntry * rangetyp,const MultirangeType * mr,Datum val)1642 multirange_contains_elem_internal(TypeCacheEntry *rangetyp,
1643 const MultirangeType *mr, Datum val)
1644 {
1645 if (MultirangeIsEmpty(mr))
1646 return false;
1647
1648 return multirange_bsearch_match(rangetyp, mr, &val,
1649 multirange_elem_bsearch_comparison);
1650 }
1651
1652 /* multirange, range -> bool functions */
1653
1654 /* contains? */
1655 Datum
multirange_contains_range(PG_FUNCTION_ARGS)1656 multirange_contains_range(PG_FUNCTION_ARGS)
1657 {
1658 MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
1659 RangeType *r = PG_GETARG_RANGE_P(1);
1660 TypeCacheEntry *typcache;
1661
1662 typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
1663
1664 PG_RETURN_BOOL(multirange_contains_range_internal(typcache->rngtype, mr, r));
1665 }
1666
1667 Datum
range_contains_multirange(PG_FUNCTION_ARGS)1668 range_contains_multirange(PG_FUNCTION_ARGS)
1669 {
1670 RangeType *r = PG_GETARG_RANGE_P(0);
1671 MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
1672 TypeCacheEntry *typcache;
1673
1674 typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
1675
1676 PG_RETURN_BOOL(range_contains_multirange_internal(typcache->rngtype, r, mr));
1677 }
1678
1679 /* contained by? */
1680 Datum
range_contained_by_multirange(PG_FUNCTION_ARGS)1681 range_contained_by_multirange(PG_FUNCTION_ARGS)
1682 {
1683 RangeType *r = PG_GETARG_RANGE_P(0);
1684 MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
1685 TypeCacheEntry *typcache;
1686
1687 typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
1688
1689 PG_RETURN_BOOL(multirange_contains_range_internal(typcache->rngtype, mr, r));
1690 }
1691
1692 Datum
multirange_contained_by_range(PG_FUNCTION_ARGS)1693 multirange_contained_by_range(PG_FUNCTION_ARGS)
1694 {
1695 MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
1696 RangeType *r = PG_GETARG_RANGE_P(1);
1697 TypeCacheEntry *typcache;
1698
1699 typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
1700
1701 PG_RETURN_BOOL(range_contains_multirange_internal(typcache->rngtype, r, mr));
1702 }
1703
1704 /*
1705 * Comparison function for checking if any range of multirange contains given
1706 * key range using binary search.
1707 */
1708 static int
multirange_range_contains_bsearch_comparison(TypeCacheEntry * typcache,RangeBound * lower,RangeBound * upper,void * key,bool * match)1709 multirange_range_contains_bsearch_comparison(TypeCacheEntry *typcache,
1710 RangeBound *lower, RangeBound *upper,
1711 void *key, bool *match)
1712 {
1713 RangeBound *keyLower = (RangeBound *) key;
1714 RangeBound *keyUpper = (RangeBound *) key + 1;
1715
1716 /* Check if key range is strictly in the left or in the right */
1717 if (range_cmp_bounds(typcache, keyUpper, lower) < 0)
1718 return -1;
1719 if (range_cmp_bounds(typcache, keyLower, upper) > 0)
1720 return 1;
1721
1722 /*
1723 * At this point we found overlapping range. But we have to check if it
1724 * really contains the key range. Anyway, we have to stop our search
1725 * here, because multirange contains only non-overlapping ranges.
1726 */
1727 *match = range_bounds_contains(typcache, lower, upper, keyLower, keyUpper);
1728
1729 return 0;
1730 }
1731
1732 /*
1733 * Test whether multirange mr contains a specific range r.
1734 */
1735 bool
multirange_contains_range_internal(TypeCacheEntry * rangetyp,const MultirangeType * mr,const RangeType * r)1736 multirange_contains_range_internal(TypeCacheEntry *rangetyp,
1737 const MultirangeType *mr,
1738 const RangeType *r)
1739 {
1740 RangeBound bounds[2];
1741 bool empty;
1742
1743 /*
1744 * Every multirange contains an infinite number of empty ranges, even an
1745 * empty one.
1746 */
1747 if (RangeIsEmpty(r))
1748 return true;
1749
1750 if (MultirangeIsEmpty(mr))
1751 return false;
1752
1753 range_deserialize(rangetyp, r, &bounds[0], &bounds[1], &empty);
1754 Assert(!empty);
1755
1756 return multirange_bsearch_match(rangetyp, mr, bounds,
1757 multirange_range_contains_bsearch_comparison);
1758 }
1759
1760 /*
1761 * Test whether range r contains a multirange mr.
1762 */
1763 bool
range_contains_multirange_internal(TypeCacheEntry * rangetyp,const RangeType * r,const MultirangeType * mr)1764 range_contains_multirange_internal(TypeCacheEntry *rangetyp,
1765 const RangeType *r,
1766 const MultirangeType *mr)
1767 {
1768 RangeBound lower1,
1769 upper1,
1770 lower2,
1771 upper2,
1772 tmp;
1773 bool empty;
1774
1775 /*
1776 * Every range contains an infinite number of empty multiranges, even an
1777 * empty one.
1778 */
1779 if (MultirangeIsEmpty(mr))
1780 return true;
1781
1782 if (RangeIsEmpty(r))
1783 return false;
1784
1785 /* Range contains multirange iff it contains its union range. */
1786 range_deserialize(rangetyp, r, &lower1, &upper1, &empty);
1787 Assert(!empty);
1788 multirange_get_bounds(rangetyp, mr, 0, &lower2, &tmp);
1789 multirange_get_bounds(rangetyp, mr, mr->rangeCount - 1, &tmp, &upper2);
1790
1791 return range_bounds_contains(rangetyp, &lower1, &upper1, &lower2, &upper2);
1792 }
1793
1794
1795 /* multirange, multirange -> bool functions */
1796
1797 /* equality (internal version) */
1798 bool
multirange_eq_internal(TypeCacheEntry * rangetyp,const MultirangeType * mr1,const MultirangeType * mr2)1799 multirange_eq_internal(TypeCacheEntry *rangetyp,
1800 const MultirangeType *mr1,
1801 const MultirangeType *mr2)
1802 {
1803 int32 range_count_1;
1804 int32 range_count_2;
1805 int32 i;
1806 RangeBound lower1,
1807 upper1,
1808 lower2,
1809 upper2;
1810
1811 /* Different types should be prevented by ANYMULTIRANGE matching rules */
1812 if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
1813 elog(ERROR, "multirange types do not match");
1814
1815 range_count_1 = mr1->rangeCount;
1816 range_count_2 = mr2->rangeCount;
1817
1818 if (range_count_1 != range_count_2)
1819 return false;
1820
1821 for (i = 0; i < range_count_1; i++)
1822 {
1823 multirange_get_bounds(rangetyp, mr1, i, &lower1, &upper1);
1824 multirange_get_bounds(rangetyp, mr2, i, &lower2, &upper2);
1825
1826 if (range_cmp_bounds(rangetyp, &lower1, &lower2) != 0 ||
1827 range_cmp_bounds(rangetyp, &upper1, &upper2) != 0)
1828 return false;
1829 }
1830
1831 return true;
1832 }
1833
1834 /* equality */
1835 Datum
multirange_eq(PG_FUNCTION_ARGS)1836 multirange_eq(PG_FUNCTION_ARGS)
1837 {
1838 MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
1839 MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
1840 TypeCacheEntry *typcache;
1841
1842 typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
1843
1844 PG_RETURN_BOOL(multirange_eq_internal(typcache->rngtype, mr1, mr2));
1845 }
1846
1847 /* inequality (internal version) */
1848 bool
multirange_ne_internal(TypeCacheEntry * rangetyp,const MultirangeType * mr1,const MultirangeType * mr2)1849 multirange_ne_internal(TypeCacheEntry *rangetyp,
1850 const MultirangeType *mr1,
1851 const MultirangeType *mr2)
1852 {
1853 return (!multirange_eq_internal(rangetyp, mr1, mr2));
1854 }
1855
1856 /* inequality */
1857 Datum
multirange_ne(PG_FUNCTION_ARGS)1858 multirange_ne(PG_FUNCTION_ARGS)
1859 {
1860 MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
1861 MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
1862 TypeCacheEntry *typcache;
1863
1864 typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
1865
1866 PG_RETURN_BOOL(multirange_ne_internal(typcache->rngtype, mr1, mr2));
1867 }
1868
1869 /* overlaps? */
1870 Datum
range_overlaps_multirange(PG_FUNCTION_ARGS)1871 range_overlaps_multirange(PG_FUNCTION_ARGS)
1872 {
1873 RangeType *r = PG_GETARG_RANGE_P(0);
1874 MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
1875 TypeCacheEntry *typcache;
1876
1877 typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
1878
1879 PG_RETURN_BOOL(range_overlaps_multirange_internal(typcache->rngtype, r, mr));
1880 }
1881
1882 Datum
multirange_overlaps_range(PG_FUNCTION_ARGS)1883 multirange_overlaps_range(PG_FUNCTION_ARGS)
1884 {
1885 MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
1886 RangeType *r = PG_GETARG_RANGE_P(1);
1887 TypeCacheEntry *typcache;
1888
1889 typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
1890
1891 PG_RETURN_BOOL(range_overlaps_multirange_internal(typcache->rngtype, r, mr));
1892 }
1893
1894 Datum
multirange_overlaps_multirange(PG_FUNCTION_ARGS)1895 multirange_overlaps_multirange(PG_FUNCTION_ARGS)
1896 {
1897 MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
1898 MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
1899 TypeCacheEntry *typcache;
1900
1901 typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
1902
1903 PG_RETURN_BOOL(multirange_overlaps_multirange_internal(typcache->rngtype, mr1, mr2));
1904 }
1905
1906 /*
1907 * Comparison function for checking if any range of multirange overlaps given
1908 * key range using binary search.
1909 */
1910 static int
multirange_range_overlaps_bsearch_comparison(TypeCacheEntry * typcache,RangeBound * lower,RangeBound * upper,void * key,bool * match)1911 multirange_range_overlaps_bsearch_comparison(TypeCacheEntry *typcache,
1912 RangeBound *lower, RangeBound *upper,
1913 void *key, bool *match)
1914 {
1915 RangeBound *keyLower = (RangeBound *) key;
1916 RangeBound *keyUpper = (RangeBound *) key + 1;
1917
1918 if (range_cmp_bounds(typcache, keyUpper, lower) < 0)
1919 return -1;
1920 if (range_cmp_bounds(typcache, keyLower, upper) > 0)
1921 return 1;
1922
1923 *match = true;
1924 return 0;
1925 }
1926
1927 bool
range_overlaps_multirange_internal(TypeCacheEntry * rangetyp,const RangeType * r,const MultirangeType * mr)1928 range_overlaps_multirange_internal(TypeCacheEntry *rangetyp,
1929 const RangeType *r,
1930 const MultirangeType *mr)
1931 {
1932 RangeBound bounds[2];
1933 bool empty;
1934
1935 /*
1936 * Empties never overlap, even with empties. (This seems strange since
1937 * they *do* contain each other, but we want to follow how ranges work.)
1938 */
1939 if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
1940 return false;
1941
1942 range_deserialize(rangetyp, r, &bounds[0], &bounds[1], &empty);
1943 Assert(!empty);
1944
1945 return multirange_bsearch_match(rangetyp, mr, bounds,
1946 multirange_range_overlaps_bsearch_comparison);
1947 }
1948
1949 bool
multirange_overlaps_multirange_internal(TypeCacheEntry * rangetyp,const MultirangeType * mr1,const MultirangeType * mr2)1950 multirange_overlaps_multirange_internal(TypeCacheEntry *rangetyp,
1951 const MultirangeType *mr1,
1952 const MultirangeType *mr2)
1953 {
1954 int32 range_count1;
1955 int32 range_count2;
1956 int32 i1;
1957 int32 i2;
1958 RangeBound lower1,
1959 upper1,
1960 lower2,
1961 upper2;
1962
1963 /*
1964 * Empties never overlap, even with empties. (This seems strange since
1965 * they *do* contain each other, but we want to follow how ranges work.)
1966 */
1967 if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
1968 return false;
1969
1970 range_count1 = mr1->rangeCount;
1971 range_count2 = mr2->rangeCount;
1972
1973 /*
1974 * Every range in mr1 gets a chance to overlap with the ranges in mr2, but
1975 * we can use their ordering to avoid O(n^2). This is similar to
1976 * range_overlaps_multirange where r1 : r2 :: mrr : r, but there if we
1977 * don't find an overlap with r we're done, and here if we don't find an
1978 * overlap with r2 we try the next r2.
1979 */
1980 i1 = 0;
1981 multirange_get_bounds(rangetyp, mr1, i1, &lower1, &upper1);
1982 for (i1 = 0, i2 = 0; i2 < range_count2; i2++)
1983 {
1984 multirange_get_bounds(rangetyp, mr2, i2, &lower2, &upper2);
1985
1986 /* Discard r1s while r1 << r2 */
1987 while (range_cmp_bounds(rangetyp, &upper1, &lower2) < 0)
1988 {
1989 if (++i1 >= range_count1)
1990 return false;
1991 multirange_get_bounds(rangetyp, mr1, i1, &lower1, &upper1);
1992 }
1993
1994 /*
1995 * If r1 && r2, we're done, otherwise we failed to find an overlap for
1996 * r2, so go to the next one.
1997 */
1998 if (range_bounds_overlaps(rangetyp, &lower1, &upper1, &lower2, &upper2))
1999 return true;
2000 }
2001
2002 /* We looked through all of mr2 without finding an overlap */
2003 return false;
2004 }
2005
2006 /* does not extend to right of? */
2007 bool
range_overleft_multirange_internal(TypeCacheEntry * rangetyp,const RangeType * r,const MultirangeType * mr)2008 range_overleft_multirange_internal(TypeCacheEntry *rangetyp,
2009 const RangeType *r,
2010 const MultirangeType *mr)
2011 {
2012 RangeBound lower1,
2013 upper1,
2014 lower2,
2015 upper2;
2016 bool empty;
2017
2018 if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
2019 PG_RETURN_BOOL(false);
2020
2021
2022 range_deserialize(rangetyp, r, &lower1, &upper1, &empty);
2023 Assert(!empty);
2024 multirange_get_bounds(rangetyp, mr, mr->rangeCount - 1,
2025 &lower2, &upper2);
2026
2027 PG_RETURN_BOOL(range_cmp_bounds(rangetyp, &upper1, &upper2) <= 0);
2028 }
2029
2030 Datum
range_overleft_multirange(PG_FUNCTION_ARGS)2031 range_overleft_multirange(PG_FUNCTION_ARGS)
2032 {
2033 RangeType *r = PG_GETARG_RANGE_P(0);
2034 MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
2035 TypeCacheEntry *typcache;
2036
2037 typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
2038
2039 PG_RETURN_BOOL(range_overleft_multirange_internal(typcache->rngtype, r, mr));
2040 }
2041
2042 Datum
multirange_overleft_range(PG_FUNCTION_ARGS)2043 multirange_overleft_range(PG_FUNCTION_ARGS)
2044 {
2045 MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
2046 RangeType *r = PG_GETARG_RANGE_P(1);
2047 TypeCacheEntry *typcache;
2048 RangeBound lower1,
2049 upper1,
2050 lower2,
2051 upper2;
2052 bool empty;
2053
2054 if (MultirangeIsEmpty(mr) || RangeIsEmpty(r))
2055 PG_RETURN_BOOL(false);
2056
2057 typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
2058
2059 multirange_get_bounds(typcache->rngtype, mr, mr->rangeCount - 1,
2060 &lower1, &upper1);
2061 range_deserialize(typcache->rngtype, r, &lower2, &upper2, &empty);
2062 Assert(!empty);
2063
2064 PG_RETURN_BOOL(range_cmp_bounds(typcache->rngtype, &upper1, &upper2) <= 0);
2065 }
2066
2067 Datum
multirange_overleft_multirange(PG_FUNCTION_ARGS)2068 multirange_overleft_multirange(PG_FUNCTION_ARGS)
2069 {
2070 MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
2071 MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
2072 TypeCacheEntry *typcache;
2073 RangeBound lower1,
2074 upper1,
2075 lower2,
2076 upper2;
2077
2078 if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
2079 PG_RETURN_BOOL(false);
2080
2081 typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
2082
2083 multirange_get_bounds(typcache->rngtype, mr1, mr1->rangeCount - 1,
2084 &lower1, &upper1);
2085 multirange_get_bounds(typcache->rngtype, mr2, mr2->rangeCount - 1,
2086 &lower2, &upper2);
2087
2088 PG_RETURN_BOOL(range_cmp_bounds(typcache->rngtype, &upper1, &upper2) <= 0);
2089 }
2090
2091 /* does not extend to left of? */
2092 bool
range_overright_multirange_internal(TypeCacheEntry * rangetyp,const RangeType * r,const MultirangeType * mr)2093 range_overright_multirange_internal(TypeCacheEntry *rangetyp,
2094 const RangeType *r,
2095 const MultirangeType *mr)
2096 {
2097 RangeBound lower1,
2098 upper1,
2099 lower2,
2100 upper2;
2101 bool empty;
2102
2103 if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
2104 PG_RETURN_BOOL(false);
2105
2106 range_deserialize(rangetyp, r, &lower1, &upper1, &empty);
2107 Assert(!empty);
2108 multirange_get_bounds(rangetyp, mr, 0, &lower2, &upper2);
2109
2110 return (range_cmp_bounds(rangetyp, &lower1, &lower2) >= 0);
2111 }
2112
2113 Datum
range_overright_multirange(PG_FUNCTION_ARGS)2114 range_overright_multirange(PG_FUNCTION_ARGS)
2115 {
2116 RangeType *r = PG_GETARG_RANGE_P(0);
2117 MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
2118 TypeCacheEntry *typcache;
2119
2120 typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
2121
2122 PG_RETURN_BOOL(range_overright_multirange_internal(typcache->rngtype, r, mr));
2123 }
2124
2125 Datum
multirange_overright_range(PG_FUNCTION_ARGS)2126 multirange_overright_range(PG_FUNCTION_ARGS)
2127 {
2128 MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
2129 RangeType *r = PG_GETARG_RANGE_P(1);
2130 TypeCacheEntry *typcache;
2131 RangeBound lower1,
2132 upper1,
2133 lower2,
2134 upper2;
2135 bool empty;
2136
2137 if (MultirangeIsEmpty(mr) || RangeIsEmpty(r))
2138 PG_RETURN_BOOL(false);
2139
2140 typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
2141
2142 multirange_get_bounds(typcache->rngtype, mr, 0, &lower1, &upper1);
2143 range_deserialize(typcache->rngtype, r, &lower2, &upper2, &empty);
2144 Assert(!empty);
2145
2146 PG_RETURN_BOOL(range_cmp_bounds(typcache->rngtype, &lower1, &lower2) >= 0);
2147 }
2148
2149 Datum
multirange_overright_multirange(PG_FUNCTION_ARGS)2150 multirange_overright_multirange(PG_FUNCTION_ARGS)
2151 {
2152 MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
2153 MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
2154 TypeCacheEntry *typcache;
2155 RangeBound lower1,
2156 upper1,
2157 lower2,
2158 upper2;
2159
2160 if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
2161 PG_RETURN_BOOL(false);
2162
2163 typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
2164
2165 multirange_get_bounds(typcache->rngtype, mr1, 0, &lower1, &upper1);
2166 multirange_get_bounds(typcache->rngtype, mr2, 0, &lower2, &upper2);
2167
2168 PG_RETURN_BOOL(range_cmp_bounds(typcache->rngtype, &lower1, &lower2) >= 0);
2169 }
2170
2171 /* contains? */
2172 Datum
multirange_contains_multirange(PG_FUNCTION_ARGS)2173 multirange_contains_multirange(PG_FUNCTION_ARGS)
2174 {
2175 MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
2176 MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
2177 TypeCacheEntry *typcache;
2178
2179 typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
2180
2181 PG_RETURN_BOOL(multirange_contains_multirange_internal(typcache->rngtype, mr1, mr2));
2182 }
2183
2184 /* contained by? */
2185 Datum
multirange_contained_by_multirange(PG_FUNCTION_ARGS)2186 multirange_contained_by_multirange(PG_FUNCTION_ARGS)
2187 {
2188 MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
2189 MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
2190 TypeCacheEntry *typcache;
2191
2192 typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
2193
2194 PG_RETURN_BOOL(multirange_contains_multirange_internal(typcache->rngtype, mr2, mr1));
2195 }
2196
2197 /*
2198 * Test whether multirange mr1 contains every range from another multirange mr2.
2199 */
2200 bool
multirange_contains_multirange_internal(TypeCacheEntry * rangetyp,const MultirangeType * mr1,const MultirangeType * mr2)2201 multirange_contains_multirange_internal(TypeCacheEntry *rangetyp,
2202 const MultirangeType *mr1,
2203 const MultirangeType *mr2)
2204 {
2205 int32 range_count1 = mr1->rangeCount;
2206 int32 range_count2 = mr2->rangeCount;
2207 int i1,
2208 i2;
2209 RangeBound lower1,
2210 upper1,
2211 lower2,
2212 upper2;
2213
2214 /*
2215 * We follow the same logic for empties as ranges: - an empty multirange
2216 * contains an empty range/multirange. - an empty multirange can't contain
2217 * any other range/multirange. - an empty multirange is contained by any
2218 * other range/multirange.
2219 */
2220
2221 if (range_count2 == 0)
2222 return true;
2223 if (range_count1 == 0)
2224 return false;
2225
2226 /*
2227 * Every range in mr2 must be contained by some range in mr1. To avoid
2228 * O(n^2) we walk through both ranges in tandem.
2229 */
2230 i1 = 0;
2231 multirange_get_bounds(rangetyp, mr1, i1, &lower1, &upper1);
2232 for (i2 = 0; i2 < range_count2; i2++)
2233 {
2234 multirange_get_bounds(rangetyp, mr2, i2, &lower2, &upper2);
2235
2236 /* Discard r1s while r1 << r2 */
2237 while (range_cmp_bounds(rangetyp, &upper1, &lower2) < 0)
2238 {
2239 if (++i1 >= range_count1)
2240 return false;
2241 multirange_get_bounds(rangetyp, mr1, i1, &lower1, &upper1);
2242 }
2243
2244 /*
2245 * If r1 @> r2, go to the next r2, otherwise return false (since every
2246 * r1[n] and r1[n+1] must have a gap). Note this will give weird
2247 * answers if you don't canonicalize, e.g. with a custom
2248 * int2multirange {[1,1], [2,2]} there is a "gap". But that is
2249 * consistent with other range operators, e.g. '[1,1]'::int2range -|-
2250 * '[2,2]'::int2range is false.
2251 */
2252 if (!range_bounds_contains(rangetyp, &lower1, &upper1,
2253 &lower2, &upper2))
2254 return false;
2255 }
2256
2257 /* All ranges in mr2 are satisfied */
2258 return true;
2259 }
2260
2261 /* strictly left of? */
2262 Datum
range_before_multirange(PG_FUNCTION_ARGS)2263 range_before_multirange(PG_FUNCTION_ARGS)
2264 {
2265 RangeType *r = PG_GETARG_RANGE_P(0);
2266 MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
2267 TypeCacheEntry *typcache;
2268
2269 typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
2270
2271 PG_RETURN_BOOL(range_before_multirange_internal(typcache->rngtype, r, mr));
2272 }
2273
2274 Datum
multirange_before_range(PG_FUNCTION_ARGS)2275 multirange_before_range(PG_FUNCTION_ARGS)
2276 {
2277 MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
2278 RangeType *r = PG_GETARG_RANGE_P(1);
2279 TypeCacheEntry *typcache;
2280
2281 typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
2282
2283 PG_RETURN_BOOL(range_after_multirange_internal(typcache->rngtype, r, mr));
2284 }
2285
2286 Datum
multirange_before_multirange(PG_FUNCTION_ARGS)2287 multirange_before_multirange(PG_FUNCTION_ARGS)
2288 {
2289 MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
2290 MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
2291 TypeCacheEntry *typcache;
2292
2293 typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
2294
2295 PG_RETURN_BOOL(multirange_before_multirange_internal(typcache->rngtype, mr1, mr2));
2296 }
2297
2298 /* strictly right of? */
2299 Datum
range_after_multirange(PG_FUNCTION_ARGS)2300 range_after_multirange(PG_FUNCTION_ARGS)
2301 {
2302 RangeType *r = PG_GETARG_RANGE_P(0);
2303 MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
2304 TypeCacheEntry *typcache;
2305
2306 typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
2307
2308 PG_RETURN_BOOL(range_after_multirange_internal(typcache->rngtype, r, mr));
2309 }
2310
2311 Datum
multirange_after_range(PG_FUNCTION_ARGS)2312 multirange_after_range(PG_FUNCTION_ARGS)
2313 {
2314 MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
2315 RangeType *r = PG_GETARG_RANGE_P(1);
2316 TypeCacheEntry *typcache;
2317
2318 typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
2319
2320 PG_RETURN_BOOL(range_before_multirange_internal(typcache->rngtype, r, mr));
2321 }
2322
2323 Datum
multirange_after_multirange(PG_FUNCTION_ARGS)2324 multirange_after_multirange(PG_FUNCTION_ARGS)
2325 {
2326 MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
2327 MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
2328 TypeCacheEntry *typcache;
2329
2330 typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
2331
2332 PG_RETURN_BOOL(multirange_before_multirange_internal(typcache->rngtype, mr2, mr1));
2333 }
2334
2335 /* strictly left of? (internal version) */
2336 bool
range_before_multirange_internal(TypeCacheEntry * rangetyp,const RangeType * r,const MultirangeType * mr)2337 range_before_multirange_internal(TypeCacheEntry *rangetyp,
2338 const RangeType *r,
2339 const MultirangeType *mr)
2340 {
2341 RangeBound lower1,
2342 upper1,
2343 lower2,
2344 upper2;
2345 bool empty;
2346
2347 if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
2348 return false;
2349
2350 range_deserialize(rangetyp, r, &lower1, &upper1, &empty);
2351 Assert(!empty);
2352
2353 multirange_get_bounds(rangetyp, mr, 0, &lower2, &upper2);
2354
2355 return (range_cmp_bounds(rangetyp, &upper1, &lower2) < 0);
2356 }
2357
2358 bool
multirange_before_multirange_internal(TypeCacheEntry * rangetyp,const MultirangeType * mr1,const MultirangeType * mr2)2359 multirange_before_multirange_internal(TypeCacheEntry *rangetyp,
2360 const MultirangeType *mr1,
2361 const MultirangeType *mr2)
2362 {
2363 RangeBound lower1,
2364 upper1,
2365 lower2,
2366 upper2;
2367
2368 if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
2369 return false;
2370
2371 multirange_get_bounds(rangetyp, mr1, mr1->rangeCount - 1,
2372 &lower1, &upper1);
2373 multirange_get_bounds(rangetyp, mr2, 0,
2374 &lower2, &upper2);
2375
2376 return (range_cmp_bounds(rangetyp, &upper1, &lower2) < 0);
2377 }
2378
2379 /* strictly right of? (internal version) */
2380 bool
range_after_multirange_internal(TypeCacheEntry * rangetyp,const RangeType * r,const MultirangeType * mr)2381 range_after_multirange_internal(TypeCacheEntry *rangetyp,
2382 const RangeType *r,
2383 const MultirangeType *mr)
2384 {
2385 RangeBound lower1,
2386 upper1,
2387 lower2,
2388 upper2;
2389 bool empty;
2390 int32 range_count;
2391
2392 if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
2393 return false;
2394
2395 range_deserialize(rangetyp, r, &lower1, &upper1, &empty);
2396 Assert(!empty);
2397
2398 range_count = mr->rangeCount;
2399 multirange_get_bounds(rangetyp, mr, range_count - 1,
2400 &lower2, &upper2);
2401
2402 return (range_cmp_bounds(rangetyp, &lower1, &upper2) > 0);
2403 }
2404
2405 bool
range_adjacent_multirange_internal(TypeCacheEntry * rangetyp,const RangeType * r,const MultirangeType * mr)2406 range_adjacent_multirange_internal(TypeCacheEntry *rangetyp,
2407 const RangeType *r,
2408 const MultirangeType *mr)
2409 {
2410 RangeBound lower1,
2411 upper1,
2412 lower2,
2413 upper2;
2414 bool empty;
2415 int32 range_count;
2416
2417 if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
2418 return false;
2419
2420 range_deserialize(rangetyp, r, &lower1, &upper1, &empty);
2421 Assert(!empty);
2422
2423 range_count = mr->rangeCount;
2424 multirange_get_bounds(rangetyp, mr, 0,
2425 &lower2, &upper2);
2426
2427 if (bounds_adjacent(rangetyp, upper1, lower2))
2428 return true;
2429
2430 if (range_count > 1)
2431 multirange_get_bounds(rangetyp, mr, range_count - 1,
2432 &lower2, &upper2);
2433
2434 if (bounds_adjacent(rangetyp, upper2, lower1))
2435 return true;
2436
2437 return false;
2438 }
2439
2440 /* adjacent to? */
2441 Datum
range_adjacent_multirange(PG_FUNCTION_ARGS)2442 range_adjacent_multirange(PG_FUNCTION_ARGS)
2443 {
2444 RangeType *r = PG_GETARG_RANGE_P(0);
2445 MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
2446 TypeCacheEntry *typcache;
2447
2448 typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
2449
2450 PG_RETURN_BOOL(range_adjacent_multirange_internal(typcache->rngtype, r, mr));
2451 }
2452
2453 Datum
multirange_adjacent_range(PG_FUNCTION_ARGS)2454 multirange_adjacent_range(PG_FUNCTION_ARGS)
2455 {
2456 MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
2457 RangeType *r = PG_GETARG_RANGE_P(1);
2458 TypeCacheEntry *typcache;
2459
2460 if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
2461 return false;
2462
2463 typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
2464
2465 PG_RETURN_BOOL(range_adjacent_multirange_internal(typcache->rngtype, r, mr));
2466 }
2467
2468 Datum
multirange_adjacent_multirange(PG_FUNCTION_ARGS)2469 multirange_adjacent_multirange(PG_FUNCTION_ARGS)
2470 {
2471 MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
2472 MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
2473 TypeCacheEntry *typcache;
2474 int32 range_count1;
2475 int32 range_count2;
2476 RangeBound lower1,
2477 upper1,
2478 lower2,
2479 upper2;
2480
2481 if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
2482 return false;
2483
2484 typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
2485
2486 range_count1 = mr1->rangeCount;
2487 range_count2 = mr2->rangeCount;
2488 multirange_get_bounds(typcache->rngtype, mr1, range_count1 - 1,
2489 &lower1, &upper1);
2490 multirange_get_bounds(typcache->rngtype, mr2, 0,
2491 &lower2, &upper2);
2492 if (bounds_adjacent(typcache->rngtype, upper1, lower2))
2493 PG_RETURN_BOOL(true);
2494
2495 if (range_count1 > 1)
2496 multirange_get_bounds(typcache->rngtype, mr1, 0,
2497 &lower1, &upper1);
2498 if (range_count2 > 1)
2499 multirange_get_bounds(typcache->rngtype, mr2, range_count2 - 1,
2500 &lower2, &upper2);
2501 if (bounds_adjacent(typcache->rngtype, upper2, lower1))
2502 PG_RETURN_BOOL(true);
2503 PG_RETURN_BOOL(false);
2504 }
2505
2506 /* Btree support */
2507
2508 /* btree comparator */
2509 Datum
multirange_cmp(PG_FUNCTION_ARGS)2510 multirange_cmp(PG_FUNCTION_ARGS)
2511 {
2512 MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
2513 MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
2514 int32 range_count_1;
2515 int32 range_count_2;
2516 int32 range_count_max;
2517 int32 i;
2518 TypeCacheEntry *typcache;
2519 int cmp = 0; /* If both are empty we'll use this. */
2520
2521 /* Different types should be prevented by ANYMULTIRANGE matching rules */
2522 if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
2523 elog(ERROR, "multirange types do not match");
2524
2525 typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
2526
2527 range_count_1 = mr1->rangeCount;
2528 range_count_2 = mr2->rangeCount;
2529
2530 /* Loop over source data */
2531 range_count_max = Max(range_count_1, range_count_2);
2532 for (i = 0; i < range_count_max; i++)
2533 {
2534 RangeBound lower1,
2535 upper1,
2536 lower2,
2537 upper2;
2538
2539 /*
2540 * If one multirange is shorter, it's as if it had empty ranges at the
2541 * end to extend its length. An empty range compares earlier than any
2542 * other range, so the shorter multirange comes before the longer.
2543 * This is the same behavior as in other types, e.g. in strings 'aaa'
2544 * < 'aaaaaa'.
2545 */
2546 if (i >= range_count_1)
2547 {
2548 cmp = -1;
2549 break;
2550 }
2551 if (i >= range_count_2)
2552 {
2553 cmp = 1;
2554 break;
2555 }
2556
2557 multirange_get_bounds(typcache->rngtype, mr1, i, &lower1, &upper1);
2558 multirange_get_bounds(typcache->rngtype, mr2, i, &lower2, &upper2);
2559
2560 cmp = range_cmp_bounds(typcache->rngtype, &lower1, &lower2);
2561 if (cmp == 0)
2562 cmp = range_cmp_bounds(typcache->rngtype, &upper1, &upper2);
2563 if (cmp != 0)
2564 break;
2565 }
2566
2567 PG_FREE_IF_COPY(mr1, 0);
2568 PG_FREE_IF_COPY(mr2, 1);
2569
2570 PG_RETURN_INT32(cmp);
2571 }
2572
2573 /* inequality operators using the multirange_cmp function */
2574 Datum
multirange_lt(PG_FUNCTION_ARGS)2575 multirange_lt(PG_FUNCTION_ARGS)
2576 {
2577 int cmp = multirange_cmp(fcinfo);
2578
2579 PG_RETURN_BOOL(cmp < 0);
2580 }
2581
2582 Datum
multirange_le(PG_FUNCTION_ARGS)2583 multirange_le(PG_FUNCTION_ARGS)
2584 {
2585 int cmp = multirange_cmp(fcinfo);
2586
2587 PG_RETURN_BOOL(cmp <= 0);
2588 }
2589
2590 Datum
multirange_ge(PG_FUNCTION_ARGS)2591 multirange_ge(PG_FUNCTION_ARGS)
2592 {
2593 int cmp = multirange_cmp(fcinfo);
2594
2595 PG_RETURN_BOOL(cmp >= 0);
2596 }
2597
2598 Datum
multirange_gt(PG_FUNCTION_ARGS)2599 multirange_gt(PG_FUNCTION_ARGS)
2600 {
2601 int cmp = multirange_cmp(fcinfo);
2602
2603 PG_RETURN_BOOL(cmp > 0);
2604 }
2605
2606 /* multirange -> range functions */
2607
2608 /* Find the smallest range that includes everything in the multirange */
2609 Datum
range_merge_from_multirange(PG_FUNCTION_ARGS)2610 range_merge_from_multirange(PG_FUNCTION_ARGS)
2611 {
2612 MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
2613 Oid mltrngtypoid = MultirangeTypeGetOid(mr);
2614 TypeCacheEntry *typcache;
2615 RangeType *result;
2616
2617 typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
2618
2619 if (MultirangeIsEmpty(mr))
2620 {
2621 result = make_empty_range(typcache->rngtype);
2622 }
2623 else if (mr->rangeCount == 1)
2624 {
2625 result = multirange_get_range(typcache->rngtype, mr, 0);
2626 }
2627 else
2628 {
2629 RangeBound firstLower,
2630 firstUpper,
2631 lastLower,
2632 lastUpper;
2633
2634 multirange_get_bounds(typcache->rngtype, mr, 0,
2635 &firstLower, &firstUpper);
2636 multirange_get_bounds(typcache->rngtype, mr, mr->rangeCount - 1,
2637 &lastLower, &lastUpper);
2638
2639 result = make_range(typcache->rngtype, &firstLower, &lastUpper, false);
2640 }
2641
2642 PG_RETURN_RANGE_P(result);
2643 }
2644
2645 /* Turn multirange into a set of ranges */
2646 Datum
multirange_unnest(PG_FUNCTION_ARGS)2647 multirange_unnest(PG_FUNCTION_ARGS)
2648 {
2649 typedef struct
2650 {
2651 MultirangeType *mr;
2652 TypeCacheEntry *typcache;
2653 int index;
2654 } multirange_unnest_fctx;
2655
2656 FuncCallContext *funcctx;
2657 multirange_unnest_fctx *fctx;
2658 MemoryContext oldcontext;
2659
2660 /* stuff done only on the first call of the function */
2661 if (SRF_IS_FIRSTCALL())
2662 {
2663 MultirangeType *mr;
2664
2665 /* create a function context for cross-call persistence */
2666 funcctx = SRF_FIRSTCALL_INIT();
2667
2668 /*
2669 * switch to memory context appropriate for multiple function calls
2670 */
2671 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
2672
2673 /*
2674 * Get the multirange value and detoast if needed. We can't do this
2675 * earlier because if we have to detoast, we want the detoasted copy
2676 * to be in multi_call_memory_ctx, so it will go away when we're done
2677 * and not before. (If no detoast happens, we assume the originally
2678 * passed multirange will stick around till then.)
2679 */
2680 mr = PG_GETARG_MULTIRANGE_P(0);
2681
2682 /* allocate memory for user context */
2683 fctx = (multirange_unnest_fctx *) palloc(sizeof(multirange_unnest_fctx));
2684
2685 /* initialize state */
2686 fctx->mr = mr;
2687 fctx->index = 0;
2688 fctx->typcache = lookup_type_cache(MultirangeTypeGetOid(mr),
2689 TYPECACHE_MULTIRANGE_INFO);
2690
2691 funcctx->user_fctx = fctx;
2692 MemoryContextSwitchTo(oldcontext);
2693 }
2694
2695 /* stuff done on every call of the function */
2696 funcctx = SRF_PERCALL_SETUP();
2697 fctx = funcctx->user_fctx;
2698
2699 if (fctx->index < fctx->mr->rangeCount)
2700 {
2701 RangeType *range;
2702
2703 range = multirange_get_range(fctx->typcache->rngtype,
2704 fctx->mr,
2705 fctx->index);
2706 fctx->index++;
2707
2708 SRF_RETURN_NEXT(funcctx, RangeTypePGetDatum(range));
2709 }
2710 else
2711 {
2712 /* do when there is no more left */
2713 SRF_RETURN_DONE(funcctx);
2714 }
2715 }
2716
2717 /* Hash support */
2718
2719 /* hash a multirange value */
2720 Datum
hash_multirange(PG_FUNCTION_ARGS)2721 hash_multirange(PG_FUNCTION_ARGS)
2722 {
2723 MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
2724 uint32 result = 1;
2725 TypeCacheEntry *typcache,
2726 *scache;
2727 int32 range_count,
2728 i;
2729
2730 typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
2731 scache = typcache->rngtype->rngelemtype;
2732 if (!OidIsValid(scache->hash_proc_finfo.fn_oid))
2733 {
2734 scache = lookup_type_cache(scache->type_id,
2735 TYPECACHE_HASH_PROC_FINFO);
2736 if (!OidIsValid(scache->hash_proc_finfo.fn_oid))
2737 ereport(ERROR,
2738 (errcode(ERRCODE_UNDEFINED_FUNCTION),
2739 errmsg("could not identify a hash function for type %s",
2740 format_type_be(scache->type_id))));
2741 }
2742
2743 range_count = mr->rangeCount;
2744 for (i = 0; i < range_count; i++)
2745 {
2746 RangeBound lower,
2747 upper;
2748 uint8 flags = MultirangeGetFlagsPtr(mr)[i];
2749 uint32 lower_hash;
2750 uint32 upper_hash;
2751 uint32 range_hash;
2752
2753 multirange_get_bounds(typcache->rngtype, mr, i, &lower, &upper);
2754
2755 if (RANGE_HAS_LBOUND(flags))
2756 lower_hash = DatumGetUInt32(FunctionCall1Coll(&scache->hash_proc_finfo,
2757 typcache->rngtype->rng_collation,
2758 lower.val));
2759 else
2760 lower_hash = 0;
2761
2762 if (RANGE_HAS_UBOUND(flags))
2763 upper_hash = DatumGetUInt32(FunctionCall1Coll(&scache->hash_proc_finfo,
2764 typcache->rngtype->rng_collation,
2765 upper.val));
2766 else
2767 upper_hash = 0;
2768
2769 /* Merge hashes of flags and bounds */
2770 range_hash = hash_uint32((uint32) flags);
2771 range_hash ^= lower_hash;
2772 range_hash = (range_hash << 1) | (range_hash >> 31);
2773 range_hash ^= upper_hash;
2774
2775 /*
2776 * Use the same approach as hash_array to combine the individual
2777 * elements' hash values:
2778 */
2779 result = (result << 5) - result + range_hash;
2780 }
2781
2782 PG_FREE_IF_COPY(mr, 0);
2783
2784 PG_RETURN_UINT32(result);
2785 }
2786
2787 /*
2788 * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
2789 * Otherwise, similar to hash_multirange.
2790 */
2791 Datum
hash_multirange_extended(PG_FUNCTION_ARGS)2792 hash_multirange_extended(PG_FUNCTION_ARGS)
2793 {
2794 MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
2795 Datum seed = PG_GETARG_DATUM(1);
2796 uint64 result = 1;
2797 TypeCacheEntry *typcache,
2798 *scache;
2799 int32 range_count,
2800 i;
2801
2802 typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
2803 scache = typcache->rngtype->rngelemtype;
2804 if (!OidIsValid(scache->hash_extended_proc_finfo.fn_oid))
2805 {
2806 scache = lookup_type_cache(scache->type_id,
2807 TYPECACHE_HASH_EXTENDED_PROC_FINFO);
2808 if (!OidIsValid(scache->hash_extended_proc_finfo.fn_oid))
2809 ereport(ERROR,
2810 (errcode(ERRCODE_UNDEFINED_FUNCTION),
2811 errmsg("could not identify a hash function for type %s",
2812 format_type_be(scache->type_id))));
2813 }
2814
2815 range_count = mr->rangeCount;
2816 for (i = 0; i < range_count; i++)
2817 {
2818 RangeBound lower,
2819 upper;
2820 uint8 flags = MultirangeGetFlagsPtr(mr)[i];
2821 uint64 lower_hash;
2822 uint64 upper_hash;
2823 uint64 range_hash;
2824
2825 multirange_get_bounds(typcache->rngtype, mr, i, &lower, &upper);
2826
2827 if (RANGE_HAS_LBOUND(flags))
2828 lower_hash = DatumGetUInt64(FunctionCall2Coll(&scache->hash_extended_proc_finfo,
2829 typcache->rngtype->rng_collation,
2830 lower.val,
2831 seed));
2832 else
2833 lower_hash = 0;
2834
2835 if (RANGE_HAS_UBOUND(flags))
2836 upper_hash = DatumGetUInt64(FunctionCall2Coll(&scache->hash_extended_proc_finfo,
2837 typcache->rngtype->rng_collation,
2838 upper.val,
2839 seed));
2840 else
2841 upper_hash = 0;
2842
2843 /* Merge hashes of flags and bounds */
2844 range_hash = DatumGetUInt64(hash_uint32_extended((uint32) flags,
2845 DatumGetInt64(seed)));
2846 range_hash ^= lower_hash;
2847 range_hash = ROTATE_HIGH_AND_LOW_32BITS(range_hash);
2848 range_hash ^= upper_hash;
2849
2850 /*
2851 * Use the same approach as hash_array to combine the individual
2852 * elements' hash values:
2853 */
2854 result = (result << 5) - result + range_hash;
2855 }
2856
2857 PG_FREE_IF_COPY(mr, 0);
2858
2859 PG_RETURN_UINT64(result);
2860 }
2861