LCOV - code coverage report
Current view: top level - src/backend/utils/adt - multirangetypes.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 93.4 % 1010 943
Test Date: 2026-05-22 00:16:36 Functions: 97.8 % 93 91
Legend: Lines:     hit not hit

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

Generated by: LCOV version 2.0-1